Merge "Use stored power state for 2.0 devices" into sc-dev
diff --git a/Android.bp b/Android.bp
index 202e548..1c9eac9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -405,6 +405,7 @@
         ":framework-statsd-sources",
         ":framework-tethering-srcs",
         ":framework-wifi-updatable-sources",
+        ":ike-srcs",
         ":updatable-media-srcs",
     ],
     visibility: ["//visibility:private"],
@@ -413,6 +414,7 @@
 java_library {
     name: "framework-updatable-stubs-module_libs_api",
     static_libs: [
+        "android.net.ipsec.ike.stubs.module_lib",
         "framework-appsearch.stubs.module_lib",
         "framework-graphics.stubs.module_lib",
         "framework-media.stubs.module_lib",
@@ -432,6 +434,7 @@
     name: "framework-all",
     installable: false,
     static_libs: [
+        "android.net.ipsec.ike.impl",
         "framework-minus-apex",
         "framework-appsearch.impl",
         "framework-graphics.impl",
@@ -536,7 +539,7 @@
         "android.hardware.vibrator-V1.3-java",
         "android.security.apc-java",
         "android.security.authorization-java",
-        "android.system.keystore2-java",
+        "android.system.keystore2-V1-java",
         "android.system.suspend.control.internal-java",
         "cameraprotosnano",
         "devicepolicyprotosnano",
@@ -760,6 +763,7 @@
     srcs: [
         ":ipconnectivity-proto-src",
         ":libstats_atom_enum_protos",
+        ":libtombstone_proto-src",
         "core/proto/**/*.proto",
         "libs/incident/**/*.proto",
         ":service-permission-protos",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..3f2e898 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
         "stable.core.platform.api.stubs",
-        // There are a few classes from modules used as type arguments that
-        // need to be resolved by metalava. For now, we can use a previously
-        // finalized stub library to resolve them. If a new class gets added,
-        // this may be need to be revisited to use a manually maintained stub
-        // library with empty classes in order to resolve those references.
-        "sdk_system_30_android",
+        // There are a few classes from modules used by the core that
+        // need to be resolved by metalava. We use a prebuilt stub of the
+        // full sdk to ensure we can resolve them. If a new class gets added,
+        // the prebuilts/sdk/current needs to be updated.
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
     ],
     high_mem: true, // Lots of sources => high memory use, see b/170701554
     installable: false,
@@ -305,6 +306,7 @@
     name: "android_stubs_current",
     srcs: [ ":api-stubs-docs-non-updatable" ],
     static_libs: [
+        "android.net.ipsec.ike.stubs",
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs",
@@ -327,6 +329,7 @@
     name: "android_system_stubs_current",
     srcs: [ ":system-api-stubs-docs-non-updatable" ],
     static_libs: [
+        "android.net.ipsec.ike.stubs.system",
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
@@ -365,6 +368,7 @@
     static_libs: [
         // Modules do not have test APIs, but we want to include their SystemApis, like we include
         // the SystemApi of framework-non-updatable-sources.
+        "android.net.ipsec.ike.stubs.system",
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
@@ -404,7 +408,11 @@
         "android_defaults_stubs_current",
         "android_stubs_dists_default",
     ],
-    libs: ["sdk_system_29_android"],
+    libs: [
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
+    ],
     static_libs: ["art.module.public.api.stubs"],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2b12da2..6c265bc 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -51,6 +51,17 @@
           "exclude-annotation": "org.junit.Ignore"
         }
       ]
+    },
+    {
+      "name": "FrameworksInProcessTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
     }
   ],
   "postsubmit-managedprofile-stress": [
diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index d3938f4..afd8e29 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -77,7 +77,7 @@
     }
 
     private void getResourcesForPath(String path) {
-        ResourcesManager.getInstance().getResources(null, path, null, null, null,
+        ResourcesManager.getInstance().getResources(null, path, null, null, null, null,
                 Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
                 null, null);
     }
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index f4c0a17..45c723b 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -95,8 +95,9 @@
                 ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
 
         Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
-                ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                c, mContext.getResources().getCompatibilityInfo(), null, null);
+                ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles,
+                Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(),
+                null, null);
         Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
 
         Resources.Theme destTheme = destResources.newTheme();
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index a3b8013..905000a 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -208,8 +208,8 @@
     ctor public GetByUriRequest.Builder();
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.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 addUri(@NonNull java.lang.String...);
-    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@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 @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
   }
@@ -243,8 +243,8 @@
 
   public static final class RemoveByUriRequest.Builder {
     ctor public RemoveByUriRequest.Builder();
-    method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...);
-    method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.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 @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
   }
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 0fcf061..656608d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -122,14 +122,14 @@
 
         /** Adds one or more URIs to the request. */
         @NonNull
-        public Builder addUri(@NonNull String... uris) {
+        public Builder addUris(@NonNull String... uris) {
             Preconditions.checkNotNull(uris);
-            return addUri(Arrays.asList(uris));
+            return addUris(Arrays.asList(uris));
         }
 
         /** Adds one or more URIs to the request. */
         @NonNull
-        public Builder addUri(@NonNull Collection<String> uris) {
+        public Builder addUris(@NonNull Collection<String> uris) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkNotNull(uris);
             mUris.addAll(uris);
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 2104198d..198eee8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -73,14 +73,14 @@
 
         /** Adds one or more URIs to the request. */
         @NonNull
-        public Builder addUri(@NonNull String... uris) {
+        public Builder addUris(@NonNull String... uris) {
             Preconditions.checkNotNull(uris);
-            return addUri(Arrays.asList(uris));
+            return addUris(Arrays.asList(uris));
         }
 
         /** Adds one or more URIs to the request. */
         @NonNull
-        public Builder addUri(@NonNull Collection<String> uris) {
+        public Builder addUris(@NonNull Collection<String> uris) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkNotNull(uris);
             mUris.addAll(uris);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 0328d54..4869aa3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -163,7 +163,7 @@
 
         /** Adds deletedTypes to the list of deleted schema types. */
         @NonNull
-        public Builder addDeletedType(@NonNull Collection<String> deletedTypes) {
+        public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
             return this;
@@ -171,7 +171,7 @@
 
         /** Adds incompatibleTypes to the list of incompatible schema types. */
         @NonNull
-        public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) {
+        public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
             return this;
@@ -179,7 +179,7 @@
 
         /** Adds migratedTypes to the list of migrated schema types. */
         @NonNull
-        public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
+        public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
             return this;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
index 1b4d284..14dd472 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
@@ -147,35 +147,41 @@
         if (bundle == null) {
             return 0;
         }
-        int[] hashCodes = new int[bundle.size()];
-        int i = 0;
+        int[] hashCodes = new int[bundle.size() + 1];
+        int hashCodeIdx = 0;
         // Bundle inherit its hashCode() from Object.java, which only relative to their memory
         // address. Bundle doesn't have an order, so we should iterate all keys and combine
         // their value's hashcode into an array. And use the hashcode of the array to be
         // the hashcode of the bundle.
-        for (String key : bundle.keySet()) {
-            Object value = bundle.get(key);
+        // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys
+        // in case the iteration order varies from run to run.
+        String[] keys = bundle.keySet().toArray(new String[0]);
+        Arrays.sort(keys);
+        // Hash the keys so we can detect key-only differences
+        hashCodes[hashCodeIdx++] = Arrays.hashCode(keys);
+        for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) {
+            Object value = bundle.get(keys[keyIdx]);
             if (value instanceof Bundle) {
-                hashCodes[i++] = deepHashCode((Bundle) value);
+                hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value);
             } else if (value instanceof int[]) {
-                hashCodes[i++] = Arrays.hashCode((int[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value);
             } else if (value instanceof byte[]) {
-                hashCodes[i++] = Arrays.hashCode((byte[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value);
             } else if (value instanceof char[]) {
-                hashCodes[i++] = Arrays.hashCode((char[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value);
             } else if (value instanceof long[]) {
-                hashCodes[i++] = Arrays.hashCode((long[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value);
             } else if (value instanceof float[]) {
-                hashCodes[i++] = Arrays.hashCode((float[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value);
             } else if (value instanceof short[]) {
-                hashCodes[i++] = Arrays.hashCode((short[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value);
             } else if (value instanceof double[]) {
-                hashCodes[i++] = Arrays.hashCode((double[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value);
             } else if (value instanceof boolean[]) {
-                hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value);
             } else if (value instanceof String[]) {
                 // Optimization to avoid Object[] handler creating an inner array for common cases
-                hashCodes[i++] = Arrays.hashCode((String[]) value);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value);
             } else if (value instanceof Object[]) {
                 Object[] array = (Object[]) value;
                 int[] innerHashCodes = new int[array.length];
@@ -186,7 +192,7 @@
                         innerHashCodes[j] = array[j].hashCode();
                     }
                 }
-                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
             } else if (value instanceof ArrayList) {
                 ArrayList<?> list = (ArrayList<?>) value;
                 int[] innerHashCodes = new int[list.size()];
@@ -198,7 +204,7 @@
                         innerHashCodes[j] = item.hashCode();
                     }
                 }
-                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
             } else if (value instanceof SparseArray) {
                 SparseArray<?> array = (SparseArray<?>) value;
                 int[] innerHashCodes = new int[array.size() * 2];
@@ -211,9 +217,9 @@
                         innerHashCodes[j * 2 + 1] = item.hashCode();
                     }
                 }
-                hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+                hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
             } else {
-                hashCodes[i++] = value.hashCode();
+                hashCodes[hashCodeIdx++] = value.hashCode();
             }
         }
         return Arrays.hashCode(hashCodes);
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 3bbc945..271129b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
@@ -49,6 +50,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /** TODO(b/142567528): add comments when implement this class */
 public class AppSearchManagerService extends SystemService {
@@ -56,6 +58,9 @@
     private PackageManagerInternal mPackageManagerInternal;
     private ImplInstanceManager mImplInstanceManager;
 
+    // Cache of unlocked user ids so we don't have to query UserManager service each time.
+    private final Set<Integer> mUnlockedUserIds = new ArraySet<>();
+
     public AppSearchManagerService(Context context) {
         super(context);
     }
@@ -67,6 +72,11 @@
         mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
     }
 
+    @Override
+    public void onUserUnlocked(@NonNull TargetUser user) {
+        mUnlockedUserIds.add(user.getUserIdentifier());
+    }
+
     private class Stub extends IAppSearchManager.Stub {
         @Override
         public void setSchema(
@@ -86,6 +96,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
                 for (int i = 0; i < schemaBundles.size(); i++) {
@@ -133,6 +144,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -165,6 +177,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
@@ -207,6 +220,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
@@ -253,6 +267,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -287,6 +302,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -318,6 +334,7 @@
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
             // opened it
             try {
+                verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
@@ -337,6 +354,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
@@ -364,6 +382,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
@@ -392,6 +411,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
@@ -431,6 +451,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -453,6 +474,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
                         mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.persistToDisk();
@@ -470,6 +492,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
+                verifyUserUnlocked(callingUserId);
                 mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
@@ -479,6 +502,13 @@
             }
         }
 
+        private void verifyUserUnlocked(int callingUserId) {
+            if (!mUnlockedUserIds.contains(callingUserId)) {
+                throw new IllegalStateException(
+                        "User " + callingUserId + " is locked or not running.");
+            }
+        }
+
         private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) {
             Preconditions.checkNotNull(callingPackage);
             if (mPackageManagerInternal.getPackageUid(
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 97b1a8c..5ea2a02 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -73,8 +73,8 @@
     /**
      * Gets an instance of AppSearchImpl for the given user.
      *
-     * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
-     * be created.
+     * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
+     * one will be created.
      *
      * @param context The context
      * @param userId The multi-user userId of the device user calling AppSearch
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 12699b7..6ba5572 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibe06fb9c574c8718191f833bb042fa10c300e4e2
+I2bf8bd9db1b71b7da4ab50dd7480e4529678413a
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 20fb909..44d5180 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -59,7 +59,7 @@
                         session.getByUri(
                                 new GetByUriRequest.Builder()
                                         .setNamespace(namespace)
-                                        .addUri(uris)
+                                        .addUris(uris)
                                         .build()));
         assertThat(result.getSuccesses()).hasSize(uris.length);
         assertThat(result.getFailures()).isEmpty();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 18856f7..82e967a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,33 +16,43 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.UserSwitchObserver;
 import android.app.job.JobInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.util.StatLogger;
 import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.job.controllers.StateController;
+import com.android.server.pm.UserManagerInternal;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -65,13 +75,18 @@
     // Try to give higher priority types lower values.
     static final int WORK_TYPE_NONE = 0;
     static final int WORK_TYPE_TOP = 1 << 0;
-    static final int WORK_TYPE_BG = 1 << 1;
-    private static final int NUM_WORK_TYPES = 2;
+    static final int WORK_TYPE_EJ = 1 << 1;
+    static final int WORK_TYPE_BG = 1 << 2;
+    static final int WORK_TYPE_BGUSER = 1 << 3;
+    @VisibleForTesting
+    static final int NUM_WORK_TYPES = 4;
 
     @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
             WORK_TYPE_NONE,
             WORK_TYPE_TOP,
-            WORK_TYPE_BG
+            WORK_TYPE_EJ,
+            WORK_TYPE_BG,
+            WORK_TYPE_BGUSER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WorkType {
@@ -94,49 +109,63 @@
 
     private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
             new WorkConfigLimitsPerMemoryTrimLevel(
-                    new WorkTypeConfig("screen_on_normal", 8,
+                    new WorkTypeConfig("screen_on_normal", 11,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6))),
-                    new WorkTypeConfig("screen_on_moderate", 8,
+                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                    ),
+                    new WorkTypeConfig("screen_on_moderate", 9,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4))),
-                    new WorkTypeConfig("screen_on_low", 5,
+                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                    ),
+                    new WorkTypeConfig("screen_on_low", 6,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
+                                    Pair.create(WORK_TYPE_BG, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1))),
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    ),
                     new WorkTypeConfig("screen_on_critical", 5,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1)))
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    )
             );
     private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
             new WorkConfigLimitsPerMemoryTrimLevel(
-                    new WorkTypeConfig("screen_off_normal", 10,
+                    new WorkTypeConfig("screen_off_normal", 13,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6))),
-                    new WorkTypeConfig("screen_off_moderate", 10,
+                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                    ),
+                    new WorkTypeConfig("screen_off_moderate", 13,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4))),
-                    new WorkTypeConfig("screen_off_low", 5,
+                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                    ),
+                    new WorkTypeConfig("screen_off_low", 7,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+                                    Pair.create(WORK_TYPE_BG, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1))),
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    ),
                     new WorkTypeConfig("screen_off_critical", 5,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1)))
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    )
             );
 
     /**
@@ -171,6 +200,10 @@
             "assignJobsToContexts",
             "refreshSystemState",
     });
+    @VisibleForTesting
+    GracePeriodObserver mGracePeriodObserver;
+    @VisibleForTesting
+    boolean mShouldRestrictBgUser;
 
     interface Stats {
         int ASSIGN_JOBS_TO_CONTEXTS = 0;
@@ -182,9 +215,13 @@
     JobConcurrencyManager(JobSchedulerService service) {
         mService = service;
         mLock = mService.mLock;
-        mContext = service.getContext();
+        mContext = service.getTestableContext();
 
         mHandler = JobSchedulerBackgroundThread.getHandler();
+
+        mGracePeriodObserver = new GracePeriodObserver(mContext);
+        mShouldRestrictBgUser = mContext.getResources().getBoolean(
+                R.bool.config_jobSchedulerRestrictBackgroundUser);
     }
 
     public void onSystemReady() {
@@ -193,10 +230,18 @@
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mReceiver, filter);
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
+        } catch (RemoteException e) {
+        }
 
         onInteractiveStateChanged(mPowerManager.isInteractive());
     }
 
+    void onUserRemoved(int userId) {
+        mGracePeriodObserver.onUserRemoved(userId);
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -224,7 +269,7 @@
                 Slog.d(TAG, "Interactive: " + interactive);
             }
 
-            final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
+            final long nowRealtime = sElapsedRealtimeClock.millis();
             if (interactive) {
                 mLastScreenOnRealtime = nowRealtime;
                 mEffectiveInteractiveState = true;
@@ -261,7 +306,7 @@
             if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
                 return;
             }
-            final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+            final long now = sElapsedRealtimeClock.millis();
             if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
                 return;
             }
@@ -723,6 +768,10 @@
             pw.print(mLastMemoryTrimLevel);
             pw.println();
 
+            pw.print("User Grace Period: ");
+            pw.print(mGracePeriodObserver.mGracePeriodExpiration);
+            pw.println();
+
             mStatLogger.dump(pw);
         } finally {
             pw.decreaseIndent();
@@ -748,15 +797,52 @@
         proto.end(token);
     }
 
+    /**
+     * Decides whether a job is from the current foreground user or the equivalent.
+     */
+    @VisibleForTesting
+    boolean shouldRunAsFgUserJob(JobStatus job) {
+        if (!mShouldRestrictBgUser) return true;
+        int userId = job.getSourceUserId();
+        UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+        UserInfo userInfo = um.getUserInfo(userId);
+
+        // If the user has a parent user (e.g. a work profile of another user), the user should be
+        // treated equivalent as its parent user.
+        if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+                && userInfo.profileGroupId != userId) {
+            userId = userInfo.profileGroupId;
+            userInfo = um.getUserInfo(userId);
+        }
+
+        int currentUser = LocalServices.getService(ActivityManagerInternal.class)
+                .getCurrentUserId();
+        // A user is treated as foreground user if any of the followings is true:
+        // 1. The user is current user
+        // 2. The user is primary user
+        // 3. The user's grace period has not expired
+        return currentUser == userId || userInfo.isPrimary()
+                || mGracePeriodObserver.isWithinGracePeriodForUser(userId);
+    }
+
     int getJobWorkTypes(@NonNull JobStatus js) {
         int classification = 0;
-        // TODO(171305774): create dedicated work type for EJ and FGS
-        if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
-                || js.shouldTreatAsExpeditedJob()) {
-            classification |= WORK_TYPE_TOP;
+        // TODO: create dedicated work type for FGS
+        if (shouldRunAsFgUserJob(js)) {
+            if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+                classification |= WORK_TYPE_TOP;
+            } else {
+                classification |= WORK_TYPE_BG;
+            }
+
+            if (js.shouldTreatAsExpeditedJob()) {
+                classification |= WORK_TYPE_EJ;
+            }
         } else {
-            classification |= WORK_TYPE_BG;
+            // TODO(171305774): create dedicated slots for EJs of bg user
+            classification |= WORK_TYPE_BGUSER;
         }
+
         return classification;
     }
 
@@ -765,9 +851,15 @@
         private static final String KEY_PREFIX_MAX_TOTAL =
                 CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
         private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+        private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
         private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
+        private static final String KEY_PREFIX_MAX_BGUSER =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
         private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+        private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
         private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
+        private static final String KEY_PREFIX_MIN_BGUSER =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
         private final String mConfigIdentifier;
 
         private int mMaxTotal;
@@ -811,10 +903,18 @@
                     properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
             mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+            final int maxEj = Math.max(1, Math.min(mMaxTotal,
+                    properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
+                            mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
+            mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj);
             final int maxBg = Math.max(1, Math.min(mMaxTotal,
                     properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
             mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+            final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
+                    properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+                            mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
+            mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser);
 
             int remaining = mMaxTotal;
             mMinReservedSlots.clear();
@@ -824,11 +924,23 @@
                             mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
             mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
             remaining -= minTop;
+            // Ensure ej is in the range [0, min(maxEj, remaining)]
+            final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
+                    properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
+                            mDefaultMinReservedSlots.get(WORK_TYPE_EJ))));
+            mMinReservedSlots.put(WORK_TYPE_EJ, minEj);
+            remaining -= minEj;
             // Ensure bg is in the range [0, min(maxBg, remaining)]
             final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
                     properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
                             mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
             mMinReservedSlots.put(WORK_TYPE_BG, minBg);
+            remaining -= minBg;
+            // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
+            final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
+                    properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+                            mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0))));
+            mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser);
         }
 
         int getMaxTotal() {
@@ -849,10 +961,18 @@
                     .println();
             pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
                     .println();
+            pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
+                    .println();
+            pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
+                    .println();
             pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
                     .println();
             pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
                     .println();
+            pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+                    mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
+            pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+                    mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
         }
     }
 
@@ -873,6 +993,58 @@
     }
 
     /**
+     * This class keeps the track of when a user's grace period expires.
+     */
+    @VisibleForTesting
+    static class GracePeriodObserver extends UserSwitchObserver {
+        // Key is UserId and Value is the time when grace period expires
+        @VisibleForTesting
+        final SparseLongArray mGracePeriodExpiration = new SparseLongArray();
+        private int mCurrentUserId;
+        @VisibleForTesting
+        int mGracePeriod;
+        private final UserManagerInternal mUserManagerInternal;
+        final Object mLock = new Object();
+
+
+        GracePeriodObserver(Context context) {
+            mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class)
+                    .getCurrentUserId();
+            mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+            mGracePeriod = Math.max(0, context.getResources().getInteger(
+                    R.integer.config_jobSchedulerUserGracePeriod));
+        }
+
+        @Override
+        public void onUserSwitchComplete(int newUserId) {
+            final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod;
+            synchronized (mLock) {
+                if (mCurrentUserId != UserHandle.USER_NULL
+                        && mUserManagerInternal.exists(mCurrentUserId)) {
+                    mGracePeriodExpiration.append(mCurrentUserId, expiration);
+                }
+                mGracePeriodExpiration.delete(newUserId);
+                mCurrentUserId = newUserId;
+            }
+        }
+
+        void onUserRemoved(int userId) {
+            synchronized (mLock) {
+                mGracePeriodExpiration.delete(userId);
+            }
+        }
+
+        @VisibleForTesting
+        public boolean isWithinGracePeriodForUser(int userId) {
+            synchronized (mLock) {
+                return userId == mCurrentUserId
+                        || sElapsedRealtimeClock.millis()
+                        < mGracePeriodExpiration.get(userId, Long.MAX_VALUE);
+            }
+        }
+    }
+
+    /**
      * This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
      * are running/pending, how many more job can start.
      *
@@ -892,20 +1064,21 @@
         private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
         private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
         private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
-        private int mNumUnspecialized = 0;
         private int mNumUnspecializedRemaining = 0;
 
         void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
             mConfigMaxTotal = workTypeConfig.getMaxTotal();
             mConfigNumReservedSlots.put(WORK_TYPE_TOP,
                     workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+            mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
             mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
+            mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
+                    workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
             mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+            mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
             mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
+            mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
 
-            mNumUnspecialized = mConfigMaxTotal;
-            mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
-            mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
             mNumUnspecializedRemaining = mConfigMaxTotal;
             for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
                 mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
@@ -934,9 +1107,15 @@
             if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
                 mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
             }
+            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+                mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1);
+            }
             if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
                 mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
             }
+            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+                mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1);
+            }
         }
 
         void stageJob(@WorkType int workType) {
@@ -1018,48 +1197,76 @@
 
         void onCountDone() {
             // Calculate how many slots to reserve for each work type. "Unspecialized" slots will
-            // be reserved for higher importance types first (ie. top before bg).
-            mNumUnspecialized = mConfigMaxTotal;
-            final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
-                    + mNumPendingJobs.get(WORK_TYPE_TOP);
-            int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
-            mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
-            mNumUnspecialized -= resTop;
-            final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
-            int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
-            mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
-            mNumUnspecialized -= resBg;
+            // be reserved for higher importance types first (ie. top before ej before bg).
+            // Steps:
+            //   1. Account for slots for already running jobs
+            //   2. Use remaining unaccounted slots to try and ensure minimum reserved slots
+            //   3. Allocate remaining up to max, based on importance
 
-            mNumUnspecializedRemaining = mNumUnspecialized;
-            // Account for already running jobs after we've assigned the minimum number of slots.
-            int unspecializedAssigned;
-            int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
-            if (extraRunning > 0) {
-                unspecializedAssigned = Math.max(0,
-                        Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
-                                extraRunning));
-                resTop += unspecializedAssigned;
-                mNumUnspecializedRemaining -= extraRunning;
-            }
-            extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
-            if (extraRunning > 0) {
-                unspecializedAssigned = Math.max(0,
-                        Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
-                resBg += unspecializedAssigned;
-                mNumUnspecializedRemaining -= extraRunning;
-            }
+            mNumUnspecializedRemaining = mConfigMaxTotal;
 
-            // Assign remaining unspecialized based on ranking.
-            unspecializedAssigned = Math.max(0,
+            // Step 1
+            int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
+            int resTop = runTop;
+            mNumUnspecializedRemaining -= resTop;
+            int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
+            int resEj = runEj;
+            mNumUnspecializedRemaining -= resEj;
+            int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
+            int resBg = runBg;
+            mNumUnspecializedRemaining -= resBg;
+            int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
+            int resBgUser = runBgUser;
+            mNumUnspecializedRemaining -= resBgUser;
+
+            // Step 2
+            final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
+            int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
+            resTop += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
+            resEj += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
+            resBg += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numBgUser,
+                            mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
+            resBgUser += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+
+            // Step 3
+            int unspecializedAssigned = Math.max(0,
                     Math.min(mNumUnspecializedRemaining,
                             Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
             mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
             mNumUnspecializedRemaining -= unspecializedAssigned;
+
+            unspecializedAssigned = Math.max(0,
+                    Math.min(mNumUnspecializedRemaining,
+                            Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
+            mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
+            mNumUnspecializedRemaining -= unspecializedAssigned;
+
             unspecializedAssigned = Math.max(0,
                     Math.min(mNumUnspecializedRemaining,
                             Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
             mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
             mNumUnspecializedRemaining -= unspecializedAssigned;
+
+            unspecializedAssigned = Math.max(0,
+                    Math.min(mNumUnspecializedRemaining,
+                            Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
+                                    - resBgUser));
+            mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
+            mNumUnspecializedRemaining -= unspecializedAssigned;
         }
 
         int canJobStart(int workTypes) {
@@ -1072,6 +1279,15 @@
                     return WORK_TYPE_TOP;
                 }
             }
+            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+                final int maxAllowed = Math.min(
+                        mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
+                        mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
+                if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
+                        < maxAllowed) {
+                    return WORK_TYPE_EJ;
+                }
+            }
             if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
                 final int maxAllowed = Math.min(
                         mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
@@ -1081,6 +1297,16 @@
                     return WORK_TYPE_BG;
                 }
             }
+            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+                final int maxAllowed = Math.min(
+                        mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
+                        mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
+                                + mNumUnspecializedRemaining);
+                if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
+                        < maxAllowed) {
+                    return WORK_TYPE_BGUSER;
+                }
+            }
             return WORK_TYPE_NONE;
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 7ce867c..82ee5d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -743,6 +743,7 @@
                         mControllers.get(c).onUserRemovedLocked(userId);
                     }
                 }
+                mConcurrencyManager.onUserRemoved(userId);
             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                 // Has this package scheduled any jobs, such that we will take action
                 // if it were to be force-stopped?
@@ -1989,8 +1990,11 @@
                 }
 
                 final boolean shouldForceBatchJob;
-                // Restricted jobs must always be batched
-                if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+                if (job.shouldTreatAsExpeditedJob()) {
+                    // Never batch expedited jobs, even for RESTRICTED apps.
+                    shouldForceBatchJob = false;
+                } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+                    // Restricted jobs must always be batched
                     shouldForceBatchJob = true;
                 } else if (job.getNumFailures() > 0) {
                     shouldForceBatchJob = false;
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 8b9990f..67fa9bb 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -29,6 +29,7 @@
   }
 
   public class MediaCommunicationManager {
+    method @IntRange(from=1) public int getVersion();
   }
 
   public class MediaController2 implements java.lang.AutoCloseable {
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index e1a8596..aefeab6 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -36,30 +36,46 @@
 import java.util.Set;
 
 /**
- * ApplicationMediaCapabilities is an immutable class that encapsulates an application's
- * capabilities for handling newer video codec format and media features.
- *
- * The ApplicationMediaCapabilities class is used by the platform to represent an application's
- * media capabilities as defined in their manifest(TODO: Add link) in order to determine
- * whether modern media files need to be transcoded for that application (TODO: Add link).
- *
- * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- * control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- * provided by applications at runtime like this override the default manifest capabilities for that
- * media access.
- *
- * <h3> Video Codec Support</h3>
- * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- * for newer format with this class as they are assumed to support older format like h.264.
- *
- * <h4>Capability of handling HDR(high dynamic range) video</h4>
- * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- * application will only need to specify individual types they supported.
+ ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
+ for handling newer video codec format and media features.
+
+ <p>
+ Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can
+ support playback of all media formats. Apps that would like to request that media be transcoded
+ into a more compatible format should declare their media capabilities in a media_capabilities
+ .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example:
+ <pre>
+ {@code
+ <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+     <format android:name="HEVC" supported="true"/>
+     <format android:name="HDR10" supported="false"/>
+     <format android:name="HDR10Plus" supported="false"/>
+ </media-capabilities>
+ }
+ </pre>
+ The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
+ represent an application's media capabilities in order to determine whether modern media files need
+ to be transcoded for that application.
+ </p>
+
+ <p>
+ ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
+ {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
+ control over the transcoding that is built into the platform. ApplicationMediaCapabilities
+ provided by applications at runtime like this override the default manifest capabilities for that
+ media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
+ through the builder class {@link ApplicationMediaCapabilities.Builder}
+
+ <h3> Video Codec Support</h3>
+ <p>
+ Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
+ for newer format with this class as they are assumed to support older format like h.264.
+
+ <h3>Capability of handling HDR(high dynamic range) video</h3>
+ <p>
+ There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
+ application will only need to specify individual types they supported.
  */
-// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
-// TODO(hkuang): Add a link to seamless transcoding detail when it is published
-// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
 public final class ApplicationMediaCapabilities implements Parcelable {
     private static final String TAG = "ApplicationMediaCapabilities";
 
@@ -105,9 +121,9 @@
      */
     public boolean isVideoMimeTypeSupported(
             @NonNull String videoMime) throws FormatNotFoundException {
-        if (mUnsupportedVideoMimeTypes.contains(videoMime)) {
+        if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
             return false;
-        } else if (mSupportedVideoMimeTypes.contains(videoMime)) {
+        } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
             return true;
         } else {
             throw new FormatNotFoundException(videoMime);
@@ -262,11 +278,27 @@
 
     /**
      * Creates {@link ApplicationMediaCapabilities} from an xml.
+     *
+     * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
+     * <p> Here is an example:
+     *
+     * <pre>
+     * {@code
+     * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+     *     <format android:name="HEVC" supported="true"/>
+     *     <format android:name="HDR10" supported="false"/>
+     *     <format android:name="HDR10Plus" supported="false"/>
+     * </media-capabilities>
+     * }
+     * </pre>
+     * <p>
+     *
      * @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
      * @return An ApplicationMediaCapabilities object.
      * @throws UnsupportedOperationException if the capabilities in xml config are invalid or
      * incompatible.
      */
+    // TODO: Add developer.android.com link for the format of the xml.
     @NonNull
     public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
         ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
@@ -430,7 +462,7 @@
                         mIsSlowMotionSupported = isSupported;
                         break;
                     default:
-                        throw new UnsupportedOperationException("Invalid format name " + name);
+                        Log.w(TAG, "Invalid format name " + name);
                 }
                 // Save the name and isSupported into the map for validate later.
                 mFormatSupportedMap.put(name, isSupported);
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b8065ef..e686076 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -15,6 +15,7 @@
  */
 package android.media;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
@@ -30,6 +31,16 @@
 public class MediaCommunicationManager {
     private static final String TAG = "MediaCommunicationManager";
 
+    /**
+     * The manager version used from beginning.
+     */
+    private static final int VERSION_1 = 1;
+
+    /**
+     * Current manager version.
+     */
+    private static final int CURRENT_VERSION = VERSION_1;
+
     private final Context mContext;
     private final IMediaCommunicationService mService;
 
@@ -43,7 +54,14 @@
         mContext = context;
         mService = IMediaCommunicationService.Stub.asInterface(
                 MediaFrameworkInitializer.getMediaServiceManager()
-                .getMediaCommunicationServiceRegisterer()
-                .get());
+                        .getMediaCommunicationServiceRegisterer()
+                        .get());
+    }
+
+    /**
+     * Gets the version of this {@link MediaCommunicationManager}.
+     */
+    public @IntRange(from = 1) int getVersion() {
+        return CURRENT_VERSION;
     }
 }
diff --git a/api/Android.bp b/api/Android.bp
index 69dce97..d5c6bf6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -28,6 +28,7 @@
 genrule {
     name: "frameworks-base-api-current.txt",
     srcs: [
+        ":android.net.ipsec.ike{.public.api.txt}",
         ":art.module.public.api{.public.api.txt}",
         ":conscrypt.module.public.api{.public.api.txt}",
         ":framework-appsearch{.public.api.txt}",
@@ -64,6 +65,7 @@
 genrule {
     name: "frameworks-base-api-current.srcjar",
     srcs: [
+        ":android.net.ipsec.ike{.public.stubs.source}",
         ":api-stubs-docs-non-updatable",
         ":art.module.public.api{.public.stubs.source}",
         ":conscrypt.module.public.api{.public.stubs.source}",
@@ -88,6 +90,7 @@
 genrule {
     name: "frameworks-base-api-removed.txt",
     srcs: [
+        ":android.net.ipsec.ike{.public.removed-api.txt}",
         ":art.module.public.api{.public.removed-api.txt}",
         ":conscrypt.module.public.api{.public.removed-api.txt}",
         ":framework-appsearch{.public.removed-api.txt}",
@@ -123,6 +126,7 @@
 genrule {
     name: "frameworks-base-api-system-current.txt",
     srcs: [
+        ":android.net.ipsec.ike{.system.api.txt}",
         ":framework-appsearch{.system.api.txt}",
         ":framework-graphics{.system.api.txt}",
         ":framework-media{.system.api.txt}",
@@ -156,6 +160,7 @@
 genrule {
     name: "frameworks-base-api-system-removed.txt",
     srcs: [
+        ":android.net.ipsec.ike{.system.removed-api.txt}",
         ":framework-appsearch{.system.removed-api.txt}",
         ":framework-graphics{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
@@ -189,6 +194,7 @@
 genrule {
     name: "frameworks-base-api-module-lib-current.txt",
     srcs: [
+        ":android.net.ipsec.ike{.module-lib.api.txt}",
         ":framework-appsearch{.module-lib.api.txt}",
         ":framework-graphics{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
@@ -221,6 +227,7 @@
 genrule {
     name: "frameworks-base-api-module-lib-removed.txt",
     srcs: [
+        ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
         ":framework-appsearch{.module-lib.removed-api.txt}",
         ":framework-graphics{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a7396fa..be82b22 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -43,7 +43,7 @@
 
 #include <android-base/properties.h>
 
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -65,6 +65,8 @@
 
 namespace android {
 
+using ui::DisplayMode;
+
 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
 static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
 static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
@@ -345,14 +347,14 @@
                         continue;
                     }
 
-                    DisplayConfig displayConfig;
-                    const status_t error = SurfaceComposerClient::getActiveDisplayConfig(
-                        mBootAnimation->mDisplayToken, &displayConfig);
+                    DisplayMode displayMode;
+                    const status_t error = SurfaceComposerClient::getActiveDisplayMode(
+                        mBootAnimation->mDisplayToken, &displayMode);
                     if (error != NO_ERROR) {
-                        SLOGE("Can't get active display configuration.");
+                        SLOGE("Can't get active display mode.");
                     }
-                    mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(),
-                        displayConfig.resolution.getHeight());
+                    mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
+                        displayMode.resolution.getHeight());
                 }
             }
         } while (numEvents > 0);
@@ -401,15 +403,15 @@
     if (mDisplayToken == nullptr)
         return NAME_NOT_FOUND;
 
-    DisplayConfig displayConfig;
+    DisplayMode displayMode;
     const status_t error =
-            SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+            SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);
     if (error != NO_ERROR)
         return error;
 
     mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
     mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
-    ui::Size resolution = displayConfig.resolution;
+    ui::Size resolution = displayMode.resolution;
     resolution = limitSurfaceSize(resolution.width, resolution.height);
     // create the native surface
     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index e21a6b2..50f4001 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -132,9 +132,6 @@
         "tests/XmlParserTests.cpp",
         "tests/ZipFileTests.cpp",
     ],
-    required: [
-        "idmap2",
-    ],
     static_libs: ["libgmock"],
     target: {
         android: {
@@ -163,9 +160,19 @@
             shared_libs: [
                 "libz",
             ],
+            data: [
+                ":libz",
+                ":idmap2",
+            ],
         },
     },
-    data: ["tests/data/**/*.apk"],
+    data: [
+        "tests/data/**/*.apk",
+    ],
+    compile_multilib: "first",
+    test_options: {
+        unit_test: true,
+    },
 }
 
 cc_binary {
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
deleted file mode 100644
index 5147f4e..0000000
--- a/cmds/idmap2/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for idmap2_tests">
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
-    </target_preparer>
-    <option name="test-suite-tag" value="idmap2_tests" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="idmap2_tests" />
-    </test>
-</configuration>
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 43a8a87..da4b255 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,7 +4,6 @@
 android.os.NullVibrator
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
 android.widget.Magnifier
-com.android.server.BootReceiver$2
 gov.nist.core.net.DefaultNetworkLayer
 android.net.rtp.AudioGroup
 android.net.rtp.AudioStream
diff --git a/core/api/current.txt b/core/api/current.txt
index 10801b0..f82e000 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -435,6 +435,7 @@
     field public static final int clickable = 16842981; // 0x10100e5
     field public static final int clipChildren = 16842986; // 0x10100ea
     field public static final int clipOrientation = 16843274; // 0x101020a
+    field public static final int clipToOutline = 16844328; // 0x1010628
     field public static final int clipToPadding = 16842987; // 0x10100eb
     field public static final int closeIcon = 16843905; // 0x1010481
     field @Deprecated public static final int codes = 16843330; // 0x1010242
@@ -1100,6 +1101,7 @@
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int previewLayout = 16844327; // 0x1010627
     field public static final int primaryContentAlpha = 16844114; // 0x1010552
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -2935,12 +2937,12 @@
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
-    field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
     field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
     field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
     field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
     field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
     field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
+    field public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; // 0x2b
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
     field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
@@ -5553,15 +5555,19 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
     field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CALL_PERSON = "android.callPerson";
     field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
     field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
     field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
     field public static final String EXTRA_COLORIZED = "android.colorized";
     field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+    field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
     field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
     field public static final String EXTRA_INFO_TEXT = "android.infoText";
     field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
@@ -5593,6 +5599,8 @@
     field public static final String EXTRA_TEXT_LINES = "android.textLines";
     field public static final String EXTRA_TITLE = "android.title";
     field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+    field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
     field public static final int FLAG_BUBBLE = 4096; // 0x1000
     field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
@@ -5841,6 +5849,14 @@
     method @NonNull public android.app.Notification.Builder setWhen(long);
   }
 
+  public static class Notification.CallStyle extends android.app.Notification.Style {
+    method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+    method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
+    method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+    method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
+  }
+
   public static final class Notification.CarExtender implements android.app.Notification.Extender {
     ctor public Notification.CarExtender();
     ctor public Notification.CarExtender(android.app.Notification);
@@ -6930,6 +6946,7 @@
     method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
     method public void addUserRestriction(@NonNull android.content.ComponentName, String);
     method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+    method public boolean canAdminGrantSensorsPermissions();
     method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
     method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName);
     method @Deprecated public void clearDeviceOwnerApp(String);
@@ -7227,6 +7244,7 @@
     field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
     field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
     field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
+    field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
     field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
     field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
     field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -8384,6 +8402,7 @@
     field public int minResizeWidth;
     field public int minWidth;
     field public int previewImage;
+    field @IdRes public int previewLayout;
     field public android.content.ComponentName provider;
     field public int resizeMode;
     field public int updatePeriodMillis;
@@ -12298,7 +12317,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
-    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12337,6 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
-    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12522,10 @@
     ctor public PackageManager.NameNotFoundException(String);
   }
 
+  @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+    method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+  }
+
   public static final class PackageManager.Property implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getBoolean();
@@ -15790,6 +15812,7 @@
     method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+    method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
   }
 
   public final class RenderNode {
@@ -20472,6 +20495,7 @@
     field public static final int QUALITY_480P = 4; // 0x4
     field public static final int QUALITY_4KDCI = 10; // 0xa
     field public static final int QUALITY_720P = 5; // 0x5
+    field public static final int QUALITY_8KUHD = 13; // 0xd
     field public static final int QUALITY_CIF = 3; // 0x3
     field public static final int QUALITY_HIGH = 1; // 0x1
     field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4
@@ -20493,6 +20517,7 @@
     field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
     field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2
     field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
+    field public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // 0x3f5
     field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb
     field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9
     field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8
@@ -30584,6 +30609,7 @@
     field public static String DIRECTORY_NOTIFICATIONS;
     field public static String DIRECTORY_PICTURES;
     field public static String DIRECTORY_PODCASTS;
+    field @NonNull public static String DIRECTORY_RECORDINGS;
     field public static String DIRECTORY_RINGTONES;
     field public static String DIRECTORY_SCREENSHOTS;
     field public static final String MEDIA_BAD_REMOVAL = "bad_removal";
@@ -40328,6 +40354,7 @@
     field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+    field public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool";
     field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
     field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
@@ -42053,7 +42080,7 @@
     method @Deprecated public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
-    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -42719,7 +42746,11 @@
   }
 
   public class ImsRcsManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
     field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
   }
 
@@ -42908,6 +42939,16 @@
     field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
   }
 
+  public final class ImsRegistrationAttributes implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAttributeFlags();
+    method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+    method public int getTransportType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
+  }
+
   public class RcsUceAdapter {
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
   }
@@ -42917,7 +42958,6 @@
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
-    field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
     field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
     field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
     field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
@@ -42926,9 +42966,9 @@
   public static class RegistrationManager.RegistrationCallback {
     ctor public RegistrationManager.RegistrationCallback();
     method @Deprecated public void onRegistered(int);
-    method public void onRegistered(int, int);
+    method public void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
     method @Deprecated public void onRegistering(int);
-    method public void onRegistering(int, int);
+    method public void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
     method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
     method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
   }
@@ -50673,6 +50713,7 @@
     method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+    field public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
     field public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE";
   }
 
@@ -51070,7 +51111,7 @@
     method public boolean sendKeyEvent(android.view.KeyEvent);
     method public boolean setComposingRegion(int, int);
     method public boolean setComposingText(CharSequence, int);
-    method public default boolean setImeTemporarilyConsumesInput(boolean);
+    method public default boolean setImeConsumesInput(boolean);
     method public boolean setSelection(int, int);
     field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
     field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
@@ -53346,7 +53387,7 @@
     method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int);
   }
 
-  public class CheckBox extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton {
     ctor public CheckBox(android.content.Context);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet);
     ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
@@ -53412,6 +53453,7 @@
     method public boolean isChecked();
     method public void setButtonDrawable(@DrawableRes int);
     method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setButtonIcon(@Nullable android.graphics.drawable.Icon);
     method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setButtonTintList(@Nullable android.content.res.ColorStateList);
     method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode);
@@ -54452,14 +54494,14 @@
     field protected String[] mExcludeMimes;
   }
 
-  public class RadioButton extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton {
     ctor public RadioButton(android.content.Context);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet, int);
     ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int);
   }
 
-  public class RadioGroup extends android.widget.LinearLayout {
+  @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout {
     ctor public RadioGroup(android.content.Context);
     ctor public RadioGroup(android.content.Context, android.util.AttributeSet);
     method public void check(@IdRes int);
@@ -54581,6 +54623,7 @@
     method public void setChronometerCountDown(@IdRes int, boolean);
     method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
     method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
+    method public void setCompoundButtonChecked(@IdRes int, boolean);
     method public void setContentDescription(@IdRes int, CharSequence);
     method public void setDisplayedChild(@IdRes int, int);
     method public void setDouble(@IdRes int, String, double);
@@ -54605,6 +54648,7 @@
     method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
     method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
     method public void setProgressBar(@IdRes int, int, int, boolean);
+    method public void setRadioGroupChecked(@IdRes int, @IdRes int);
     method public void setRelativeScrollPosition(@IdRes int, int);
     method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
     method public void setRemoteAdapter(@IdRes int, android.content.Intent);
@@ -54617,6 +54661,8 @@
     method public void setTextViewText(@IdRes int, CharSequence);
     method public void setTextViewTextSize(@IdRes int, int, float);
     method public void setUri(@IdRes int, String, android.net.Uri);
+    method public void setViewOutlinePreferredRadius(@IdRes int, float, int);
+    method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
     method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
     method public void setViewVisibility(@IdRes int, int);
     method public void showNext(@IdRes int);
@@ -54641,6 +54687,12 @@
   @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface RemoteViews.RemoteView {
   }
 
+  public static final class RemoteViews.RemoteViewOutlineProvider extends android.view.ViewOutlineProvider {
+    ctor public RemoteViews.RemoteViewOutlineProvider(float);
+    method public void getOutline(@NonNull android.view.View, @NonNull android.graphics.Outline);
+    method public float getRadius();
+  }
+
   public abstract class RemoteViewsService extends android.app.Service {
     ctor public RemoteViewsService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -54971,7 +55023,7 @@
     ctor public StackView(android.content.Context, android.util.AttributeSet, int, int);
   }
 
-  public class Switch extends android.widget.CompoundButton {
+  @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton {
     ctor public Switch(android.content.Context);
     ctor public Switch(android.content.Context, android.util.AttributeSet);
     ctor public Switch(android.content.Context, android.util.AttributeSet, int);
@@ -55002,12 +55054,14 @@
     method public void setTextOff(CharSequence);
     method public void setTextOn(CharSequence);
     method public void setThumbDrawable(android.graphics.drawable.Drawable);
+    method public void setThumbIcon(@Nullable android.graphics.drawable.Icon);
     method public void setThumbResource(@DrawableRes int);
     method public void setThumbTextPadding(int);
     method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setThumbTintList(@Nullable android.content.res.ColorStateList);
     method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode);
     method public void setTrackDrawable(android.graphics.drawable.Drawable);
+    method public void setTrackIcon(@Nullable android.graphics.drawable.Icon);
     method public void setTrackResource(@DrawableRes int);
     method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode);
     method public void setTrackTintList(@Nullable android.content.res.ColorStateList);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bf70803..4e25625 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -162,6 +162,7 @@
   }
 
   public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     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);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1977bab..74ccbb1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -290,6 +290,7 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+    field public static final int hotwordDetectionService = 16844326; // 0x1010626
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -869,6 +870,7 @@
   }
 
   public class DevicePolicyManager {
+    method public boolean canAdminGrantSensorsPermissionsForUser(int);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -1988,6 +1990,7 @@
     field public static final int UUID_BYTES_128_BIT = 16; // 0x10
     field public static final int UUID_BYTES_16_BIT = 2; // 0x2
     field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+    field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
   }
 
   public final class BufferConstraint implements android.os.Parcelable {
@@ -4664,6 +4667,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4672,6 +4676,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
   }
 
   public final class LocationRequest implements android.os.Parcelable {
@@ -4821,6 +4826,10 @@
     method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
   }
 
+  public static interface ProviderRequest.Listener {
+    method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
+  }
+
 }
 
 package android.media {
@@ -4970,6 +4979,7 @@
     method public android.media.PlayerProxy getPlayerProxy();
     method public int getPlayerState();
     method public int getPlayerType();
+    method @IntRange(from=0) public int getSessionId();
     method public boolean isActive();
     field public static final int PLAYER_STATE_IDLE = 1; // 0x1
     field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
@@ -8642,6 +8652,7 @@
     method @NonNull public static String formatUid(int);
     method public static int getAppId(int);
     method public int getIdentifier();
+    method public static int getUid(@NonNull android.os.UserHandle, int);
     method @Deprecated public boolean isOwner();
     method public boolean isSystem();
     method public static int myUserId();
@@ -9509,6 +9520,11 @@
     method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
   }
 
+  public abstract class KeyProperties {
+    field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+    field public static final int NAMESPACE_WIFI = 102; // 0x66
+  }
+
 }
 
 package android.security.keystore.recovery {
@@ -10331,7 +10347,7 @@
     ctor public ExternalStorageService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
-    method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+    method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
     method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
     method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
     field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11704,6 +11720,7 @@
     method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11711,6 +11728,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
@@ -11851,6 +11869,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+    method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11975,6 +11994,9 @@
     field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
     field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
     field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
+    field public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; // 0x2
+    field public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; // 0x1
+    field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0
     field public static final int RADIO_POWER_OFF = 0; // 0x0
     field public static final int RADIO_POWER_ON = 1; // 0x1
     field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -12941,10 +12963,31 @@
     method @Deprecated public void onRegistering(int);
   }
 
+  public class ImsRcsManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener);
+  }
+
+  public static interface ImsRcsManager.OnAvailabilityChangedListener {
+    method public void onAvailabilityChanged(int);
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
 
+  public final class ImsRegistrationAttributes implements android.os.Parcelable {
+    method public int getRegistrationTechnology();
+  }
+
+  public static final class ImsRegistrationAttributes.Builder {
+    ctor public ImsRegistrationAttributes.Builder(int);
+    method @NonNull public android.telephony.ims.ImsRegistrationAttributes build();
+    method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>);
+  }
+
   public class ImsService extends android.app.Service {
     ctor public ImsService();
     method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -13581,11 +13624,24 @@
     ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
     method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
+    method public boolean queryCapabilityConfiguration(int, int);
+    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
     method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
   }
 
+  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+    ctor public RcsFeature.RcsImsCapabilities(int);
+    method public void addCapabilities(int);
+    method public boolean isCapable(int);
+    method public void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+  }
+
 }
 
 package android.telephony.ims.stub {
@@ -13715,7 +13771,9 @@
     ctor public ImsRegistrationImplBase();
     method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
     method public final void onRegistered(int);
+    method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
     method public final void onRegistering(int);
+    method public final void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
     method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
     method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
     method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f011983..e39b2b8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -12,6 +12,7 @@
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
+    field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
@@ -392,11 +393,10 @@
     method public boolean isFactoryResetProtectionPolicySupported();
     method @NonNull public static String operationToString(int);
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
     method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
     method @NonNull public static String unsafeOperationReasonToString(int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
-    field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
-    field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
     field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -464,6 +464,7 @@
   }
 
   public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+    method public boolean canDeviceOwnerGrantSensorsPermissions();
     method public int describeContents();
     method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
     method public long getLocalTime();
@@ -478,6 +479,7 @@
   public static final class FullyManagedDeviceProvisioningParams.Builder {
     ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
     method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+    method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
     method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
     method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
     method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
@@ -901,6 +903,40 @@
 
 }
 
+package android.hardware.devicestate {
+
+  public final class DeviceStateManager {
+    method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
+    method @NonNull public int[] getSupportedStates();
+    method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+  }
+
+  public static interface DeviceStateManager.DeviceStateListener {
+    method public void onDeviceStateChanged(int);
+  }
+
+  public final class DeviceStateRequest {
+    method public int getFlags();
+    method public int getState();
+    method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int);
+    field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1
+  }
+
+  public static final class DeviceStateRequest.Builder {
+    method @NonNull public android.hardware.devicestate.DeviceStateRequest build();
+    method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int);
+  }
+
+  public static interface DeviceStateRequest.Callback {
+    method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest);
+    method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest);
+    method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest);
+  }
+
+}
+
 package android.hardware.display {
 
   public class AmbientDisplayConfiguration {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74..af5df76 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@
     name: "IDropBoxManagerService.aidl",
     srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
 }
+
+filegroup {
+    name: "ITracingServiceProxy.aidl",
+    srcs: ["android/tracing/ITracingServiceProxy.aidl"],
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 4b2d741..768ec38 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,12 +20,12 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -97,10 +97,10 @@
           GESTURE_UNKNOWN,
           GESTURE_TOUCH_EXPLORATION,
             GESTURE_2_FINGER_SINGLE_TAP,
-            GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
             GESTURE_2_FINGER_DOUBLE_TAP,
             GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_2_FINGER_TRIPLE_TAP,
+            GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD,
             GESTURE_3_FINGER_SINGLE_TAP,
             GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
             GESTURE_3_FINGER_DOUBLE_TAP,
@@ -232,8 +232,8 @@
             case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
             case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
             case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
-            case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
-                return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
+            case GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD:
+                return "GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD";
             case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
             case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
                 return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1fa7fa2..dab4a5d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -445,8 +445,8 @@
     /** The user has performed a three-finger double tap and hold gesture on the touch screen. */
     public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
 
-    /** The user has performed a two-finger  single-tap and hold gesture on the touch screen. */
-    public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+    /** The user has performed a two-finger  triple-tap and hold gesture on the touch screen. */
+    public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43;
 
     /** The user has performed a three-finger  single-tap and hold gesture on the touch screen. */
     public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9b6f4b4..c31c22c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -538,4 +538,10 @@
      * @return mBootTimeTempAllowlistDuration of ActivityManagerConstants.
      */
     public abstract long getBootTimeTempAllowListDuration();
+
+    /** Register an {@link AnrController} to control the ANR dialog behavior */
+    public abstract void registerAnrController(AnrController controller);
+
+    /** Unregister an {@link AnrController} */
+    public abstract void unregisterAnrController(AnrController controller);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e5a04c9..bb6a774 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2289,11 +2289,12 @@
      * Creates the top level resources for the given package. Will return an existing
      * Resources if one has already been created.
      */
-    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
-            String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) {
-        return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
-                null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(),
-                null);
+    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs,
+                    String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo,
+                    Configuration overrideConfig) {
+        return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs,
+                overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(),
+                pkgInfo.getClassLoader(), null);
     }
 
     @UnsupportedAppUsage
@@ -2462,12 +2463,15 @@
     private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk,
             ApplicationInfo appInfo) {
         Resources packageResources = loadedApk.mResources;
-        String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs());
-        String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs);
+        boolean resourceDirsUpToDate = Arrays.equals(
+                ArrayUtils.defeatNullable(appInfo.resourceDirs),
+                ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()));
+        boolean overlayPathsUpToDate = Arrays.equals(
+                ArrayUtils.defeatNullable(appInfo.overlayPaths),
+                ArrayUtils.defeatNullable(loadedApk.getOverlayPaths()));
 
         return (packageResources == null || packageResources.getAssets().isUpToDate())
-                && overlayDirs.length == resourceDirs.length
-                && ArrayUtils.containsAll(overlayDirs, resourceDirs);
+                && resourceDirsUpToDate && overlayPathsUpToDate;
     }
 
     @UnsupportedAppUsage
@@ -5685,6 +5689,13 @@
         final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                 amOverrideConfig, contextThemeWrapperOverrideConfig);
         mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
+        final Resources res = activity.getResources();
+        if (res.hasOverrideDisplayAdjustments()) {
+            // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
+            // configuration of activity may be sent later than the adjustments. In this case, the
+            // adjustments need to be updated for the consistency of display info.
+            res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
+        }
 
         activity.mConfigChangeFlags = 0;
         activity.mCurrentConfig = new Configuration(newConfig);
diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java
new file mode 100644
index 0000000..cfc9d27
--- /dev/null
+++ b/core/java/android/app/AnrController.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Interface to control the ANR dialog within the activity manager
+ * {@hide}
+ */
+public interface AnrController {
+    /**
+     * Returns the delay in milliseconds for an ANR dialog that is about to be shown for
+     * {@code packageName}.
+     */
+    long getAnrDelayMillis(String packageName, int uid);
+}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..062cab4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.Checksum;
 import android.content.pm.ComponentInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
     @Override
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(trustedInstallers);
         try {
             if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
                         "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
                                 + "list of certificates.");
             }
+            IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+                    new IOnChecksumsReadyListener.Stub() {
+                        @Override
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            onChecksumsReadyListener.onChecksumsReady(checksums);
+                        }
+                    };
             mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
-                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+                    encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+                    getUserId());
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
             throw new RuntimeException(e);
@@ -1717,7 +1728,7 @@
         final Resources r = mContext.mMainThread.getTopLevelResources(
                 sameUid ? app.sourceDir : app.publicSourceDir,
                 sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
-                app.resourceDirs, app.sharedLibraryFiles,
+                app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles,
                 mContext.mPackageInfo, configuration);
         if (r != null) {
             return r;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4ddeb8f..9a20e0f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2345,6 +2345,7 @@
                 pi.getResDir(),
                 splitResDirs,
                 pi.getOverlayDirs(),
+                pi.getOverlayPaths(),
                 pi.getApplicationInfo().sharedLibraryFiles,
                 overrideDisplayId,
                 overrideConfig,
@@ -2442,6 +2443,7 @@
                 mPackageInfo.getResDir(),
                 paths,
                 mPackageInfo.getOverlayDirs(),
+                mPackageInfo.getOverlayPaths(),
                 mPackageInfo.getApplicationInfo().sharedLibraryFiles,
                 mForceDisplayOverrideInResources ? getDisplayId() : null,
                 null,
@@ -2558,7 +2560,8 @@
     Resources createWindowContextResources() {
         final String resDir = mPackageInfo.getResDir();
         final String[] splitResDirs = mPackageInfo.getSplitResDirs();
-        final String[] overlayDirs = mPackageInfo.getOverlayDirs();
+        final String[] legacyOverlayDirs = mPackageInfo.getOverlayDirs();
+        final String[] overlayPaths = mPackageInfo.getOverlayPaths();
         final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles;
         final int displayId = getDisplayId();
         final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
@@ -2567,7 +2570,7 @@
         final List<ResourcesLoader> loaders = mResources.getLoaders();
 
         return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs,
-                overlayDirs, libDirs, displayId, null /* overrideConfig */,
+                legacyOverlayDirs, overlayPaths, libDirs, displayId, null /* overrideConfig */,
                 compatInfo, mClassLoader, loaders);
     }
 
@@ -2855,6 +2858,7 @@
                 packageInfo.getResDir(),
                 splitDirs,
                 packageInfo.getOverlayDirs(),
+                packageInfo.getOverlayPaths(),
                 packageInfo.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfiguration,
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 8b6570f..ac1fa1e 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -16,7 +16,9 @@
 
 package android.app;
 
+import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
 import android.content.Context;
@@ -73,8 +75,8 @@
     /**
      * Returns the game mode for the given package.
      */
-    // TODO(b/178111358): Add @RequiresPermission.
     @UserHandleAware
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @GameMode int getGameMode(String packageName) {
         try {
             return mService.getGameMode(packageName, mContext.getUserId());
@@ -86,8 +88,8 @@
     /**
      * Sets the game mode for the given package.
      */
-    // TODO(b/178111358): Add @RequiresPermission.
     @UserHandleAware
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public void setGameMode(String packageName, @GameMode int gameMode) {
         try {
             mService.setGameMode(packageName, gameMode, mContext.getUserId());
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c01b5a3..be426aa 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -113,7 +113,8 @@
     private String mAppDir;
     @UnsupportedAppUsage
     private String mResDir;
-    private String[] mOverlayDirs;
+    private String[] mLegacyOverlayDirs;
+    private String[] mOverlayPaths;
     @UnsupportedAppUsage
     private String mDataDir;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -222,7 +223,8 @@
         mSplitAppDirs = null;
         mSplitResDirs = null;
         mSplitClassLoaderNames = null;
-        mOverlayDirs = null;
+        mLegacyOverlayDirs = null;
+        mOverlayPaths = null;
         mDataDir = null;
         mDataDirFile = null;
         mDeviceProtectedDataDirFile = null;
@@ -364,8 +366,8 @@
                 }
 
                 mResources = ResourcesManager.getInstance().getResources(null, mResDir,
-                        splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                        null, null, getCompatibilityInfo(),
+                        splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+                        mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
                         getClassLoader(), mApplication == null ? null
                                 : mApplication.getResources().getLoaders());
             }
@@ -379,7 +381,8 @@
         mApplicationInfo = aInfo;
         mAppDir = aInfo.sourceDir;
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
-        mOverlayDirs = aInfo.resourceDirs;
+        mLegacyOverlayDirs = aInfo.resourceDirs;
+        mOverlayPaths = aInfo.overlayPaths;
         mDataDir = aInfo.dataDir;
         mLibDir = aInfo.nativeLibraryDir;
         mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
@@ -1213,9 +1216,19 @@
         return mSplitResDirs;
     }
 
+    /**
+     * Corresponds to {@link ApplicationInfo#resourceDirs}.
+     */
     @UnsupportedAppUsage
     public String[] getOverlayDirs() {
-        return mOverlayDirs;
+        return mLegacyOverlayDirs;
+    }
+
+    /**
+     * Corresponds to {@link ApplicationInfo#overlayPaths}.
+     */
+    public String[] getOverlayPaths() {
+        return mOverlayPaths;
     }
 
     public String getDataDir() {
@@ -1252,8 +1265,8 @@
             }
 
             mResources = ResourcesManager.getInstance().getResources(null, mResDir,
-                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                    null, null, getCompatibilityInfo(),
+                    splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+                    mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
                     getClassLoader(), null);
         }
         return mResources;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 49f508d..c242fd4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -22,7 +22,10 @@
 
 import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.ColorInt;
+import android.annotation.ColorRes;
 import android.annotation.DimenRes;
 import android.annotation.Dimension;
 import android.annotation.DrawableRes;
@@ -33,6 +36,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringRes;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -403,6 +407,7 @@
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_call);
         STANDARD_LAYOUTS.add(R.layout.notification_template_header);
     }
 
@@ -649,7 +654,7 @@
     private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
             BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
             DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
-            MessagingStyle.class);
+            MessagingStyle.class, CallStyle.class);
 
     /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
@@ -1318,6 +1323,53 @@
     public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
 
     /**
+     * {@link #extras} key: the type of call represented by the
+     * {@link android.app.Notification.CallStyle} notification. This extra is an int.
+     * @hide
+     */
+    public static final String EXTRA_CALL_TYPE = "android.callType";
+
+    /**
+     * {@link #extras} key: the person to be displayed as calling for the
+     * {@link android.app.Notification.CallStyle} notification. This extra is a {@link Person}.
+     */
+    public static final String EXTRA_CALL_PERSON = "android.callPerson";
+
+    /**
+     * {@link #extras} key: the icon to be displayed as a verification status of the caller on a
+     * {@link android.app.Notification.CallStyle} notification. This extra is an {@link Icon}.
+     */
+    public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+
+    /**
+     * {@link #extras} key: the text to be displayed as a verification status of the caller on a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a
+     * {@link CharSequence}.
+     */
+    public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+
+    /**
+     * {@link #extras} key: the intent to be sent when the users answers a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a
+     * {@link PendingIntent}.
+     */
+    public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+
+    /**
+     * {@link #extras} key: the intent to be sent when the users declines a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a
+     * {@link PendingIntent}.
+     */
+    public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+
+    /**
+     * {@link #extras} key: the intent to be sent when the users hangs up a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a
+     * {@link PendingIntent}.
+     */
+    public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+
+    /**
      * {@link #extras} key: whether the notification should be colorized as
      * supplied to {@link Builder#setColorized(boolean)}.
      */
@@ -5876,11 +5928,11 @@
             return summary;
         }
 
-        private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
+        private RemoteViews generateActionButton(Action action, boolean emphasizedMode,
                 StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    emphazisedMode ? getEmphasizedActionLayoutResource()
+                    emphasizedMode ? getEmphasizedActionLayoutResource()
                             : tombstone ? getActionTombstoneLayoutResource()
                                     : getActionLayoutResource());
             if (!tombstone) {
@@ -5890,43 +5942,42 @@
             if (action.mRemoteInputs != null) {
                 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
             }
-            if (emphazisedMode) {
+            if (emphasizedMode) {
                 // change the background bgColor
                 CharSequence title = action.title;
-                ColorStateList[] outResultColor = null;
+                ColorStateList[] outResultColor = new ColorStateList[1];
                 int background = resolveBackgroundColor(p);
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
-                    outResultColor = new ColorStateList[1];
                     title = ensureColorSpanContrast(title, background, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
-                setTextViewColorPrimary(button, R.id.action0, p);
-                int rippleColor;
-                boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
+                int textColor = getPrimaryTextColor(p);
+                boolean hasColorOverride = outResultColor[0] != null;
                 if (hasColorOverride) {
                     // There's a span spanning the full text, let's take it and use it as the
                     // background color
                     background = outResultColor[0].getDefaultColor();
-                    int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+                    textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                             background, mInNightMode);
-                    button.setTextColor(R.id.action0, textColor);
-                    rippleColor = textColor;
                 } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
                         && mTintActionButtons && !mInNightMode) {
-                    rippleColor = resolveContrastColor(p);
-                    button.setTextColor(R.id.action0, rippleColor);
-                } else {
-                    rippleColor = getPrimaryTextColor(p);
+                    textColor = resolveContrastColor(p);
                 }
+                button.setTextColor(R.id.action0, textColor);
                 // We only want about 20% alpha for the ripple
-                rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
+                final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
                 button.setColorStateList(R.id.action0, "setRippleColor",
                         ColorStateList.valueOf(rippleColor));
                 button.setColorStateList(R.id.action0, "setButtonBackground",
                         ColorStateList.valueOf(background));
                 button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
+                if (p.mAllowActionIcons) {
+                    button.setImageViewIcon(R.id.action0, action.getIcon());
+                    boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
+                    button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+                }
             } else {
                 button.setTextViewText(R.id.action0, processTextSpans(
                         processLegacyText(action.title)));
@@ -5936,8 +5987,12 @@
                     button.setTextColor(R.id.action0, resolveContrastColor(p));
                 }
             }
-            button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
-                    mActions.indexOf(action));
+            // CallStyle notifications add action buttons which don't actually exist in mActions,
+            //  so we have to omit the index in that case.
+            int actionIndex = mActions.indexOf(action);
+            if (actionIndex != -1) {
+                button.setIntTag(R.id.action0, R.id.notification_action_index_tag, actionIndex);
+            }
             return button;
         }
 
@@ -6371,6 +6426,10 @@
             return R.layout.notification_template_material_conversation;
         }
 
+        private int getCallLayoutResource() {
+            return R.layout.notification_template_material_call;
+        }
+
         private int getActionLayoutResource() {
             return R.layout.notification_material_action;
         }
@@ -8039,6 +8098,10 @@
                             : mBuilder.getMessagingLayoutResource(),
                     p,
                     bindResult);
+            if (isConversationLayout) {
+                mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+                mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+            }
 
             addExtras(mBuilder.mN.extras);
             if (!isConversationLayout) {
@@ -8925,6 +8988,441 @@
         }
     }
 
+
+
+    /**
+     * Helper class for generating large-format notifications that include a large image attachment.
+     *
+     * Here's how you'd set the <code>CallStyle</code> on a notification:
+     * <pre class="prettyprint">
+     * Notification notif = new Notification.Builder(mContext)
+     *     .setSmallIcon(R.drawable.new_post)
+     *     .setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent))
+     *     .build();
+     * </pre>
+     */
+    public static class CallStyle extends Style {
+        private static final int CALL_TYPE_INCOMING = 1;
+        private static final int CALL_TYPE_ONGOING = 2;
+        private static final int CALL_TYPE_SCREENING = 3;
+
+        /**
+         * This is a key used privately on the action.extras to give spacing priority
+         * to the required call actions
+         */
+        private static final String KEY_ACTION_PRIORITY = "key_action_priority";
+
+        private int mCallType;
+        private Person mPerson;
+        private PendingIntent mAnswerIntent;
+        private PendingIntent mDeclineIntent;
+        private PendingIntent mHangUpIntent;
+        private Icon mVerificationIcon;
+        private CharSequence mVerificationText;
+
+        CallStyle() {
+        }
+
+        /**
+         * Create a CallStyle for an incoming call.
+         * This notification will have a decline and an answer action, will allow a single
+         * custom {@link Builder#addAction(Action) action}, and will have a default
+         * {@link Builder#setContentText(CharSequence) content text} for an incoming call.
+         *
+         * @param person        The person displayed as the caller.
+         *                      The person also needs to have a non-empty name associated with it.
+         * @param declineIntent The intent to be sent when the user taps the decline action
+         * @param answerIntent  The intent to be sent when the user taps the answer action
+         */
+        @NonNull
+        public static CallStyle forIncomingCall(@NonNull Person person,
+                @NonNull PendingIntent declineIntent, @NonNull PendingIntent answerIntent) {
+            return new CallStyle(CALL_TYPE_INCOMING, person,
+                    null /* hangUpIntent */,
+                    requireNonNull(declineIntent, "declineIntent is required"),
+                    requireNonNull(answerIntent, "answerIntent is required")
+            );
+        }
+
+        /**
+         * Create a CallStyle for an ongoing call.
+         * This notification will have a hang up action, will allow up to two
+         * custom {@link Builder#addAction(Action) actions}, and will have a default
+         * {@link Builder#setContentText(CharSequence) content text} for an ongoing call.
+         *
+         * @param person       The person displayed as being on the other end of the call.
+         *                     The person also needs to have a non-empty name associated with it.
+         * @param hangUpIntent The intent to be sent when the user taps the hang up action
+         */
+        @NonNull
+        public static CallStyle forOngoingCall(@NonNull Person person,
+                @NonNull PendingIntent hangUpIntent) {
+            return new CallStyle(CALL_TYPE_ONGOING, person,
+                    requireNonNull(hangUpIntent, "hangUpIntent is required"),
+                    null /* declineIntent */,
+                    null /* answerIntent */
+            );
+        }
+
+        /**
+         * Create a CallStyle for a call that is being screened.
+         * This notification will have a hang up and an answer action, will allow a single
+         * custom {@link Builder#addAction(Action) action}, and will have a default
+         * {@link Builder#setContentText(CharSequence) content text} for a call that is being
+         * screened.
+         *
+         * @param person       The person displayed as the caller.
+         *                     The person also needs to have a non-empty name associated with it.
+         * @param hangUpIntent The intent to be sent when the user taps the hang up action
+         * @param answerIntent The intent to be sent when the user taps the answer action
+         */
+        @NonNull
+        public static CallStyle forScreeningCall(@NonNull Person person,
+                @NonNull PendingIntent hangUpIntent, @NonNull PendingIntent answerIntent) {
+            return new CallStyle(CALL_TYPE_SCREENING, person,
+                    requireNonNull(hangUpIntent, "hangUpIntent is required"),
+                    null /* declineIntent */,
+                    requireNonNull(answerIntent, "answerIntent is required")
+            );
+        }
+
+        /**
+         * @param person The person displayed for the incoming call.
+         *             The user also needs to have a non-empty name associated with it.
+         * @param hangUpIntent The intent to be sent when the user taps the hang up action
+         * @param declineIntent The intent to be sent when the user taps the decline action
+         * @param answerIntent The intent to be sent when the user taps the answer action
+         */
+        private CallStyle(int callType, @NonNull Person person,
+                @Nullable PendingIntent hangUpIntent, @Nullable PendingIntent declineIntent,
+                @Nullable PendingIntent answerIntent) {
+            if (person == null || TextUtils.isEmpty(person.getName())) {
+                throw new IllegalArgumentException("person must have a non-empty a name");
+            }
+            mCallType = callType;
+            mPerson = person;
+            mAnswerIntent = answerIntent;
+            mDeclineIntent = declineIntent;
+            mHangUpIntent = hangUpIntent;
+        }
+
+        /**
+         * Optional icon to be displayed with {@link #setVerificationText(CharSequence) text}
+         * as a verification status of the caller.
+         */
+        @NonNull
+        public CallStyle setVerificationIcon(@Nullable Icon verificationIcon) {
+            mVerificationIcon = verificationIcon;
+            return this;
+        }
+
+        /**
+         * Optional text to be displayed with an {@link #setVerificationIcon(Icon) icon}
+         * as a verification status of the caller.
+         */
+        @NonNull
+        public CallStyle setVerificationText(@Nullable CharSequence verificationText) {
+            mVerificationText = safeCharSequence(verificationText);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean displayCustomViewInline() {
+            // This is a lie; True is returned to make sure that the custom view is not used
+            // instead of the template, but it will not actually be included.
+            return true;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void purgeResources() {
+            super.purgeResources();
+            if (mVerificationIcon != null) {
+                mVerificationIcon.convertToAshmem();
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void reduceImageSizes(Context context) {
+            super.reduceImageSizes(context);
+            if (mVerificationIcon != null) {
+                int rightIconSize = context.getResources().getDimensionPixelSize(
+                        ActivityManager.isLowRamDeviceStatic()
+                                ? R.dimen.notification_right_icon_size_low_ram
+                                : R.dimen.notification_right_icon_size);
+                mVerificationIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeContentView(boolean increasedHeight) {
+            return makeCallLayout();
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+            return makeCallLayout();
+        }
+
+        /**
+         * @hide
+         */
+        public RemoteViews makeBigContentView() {
+            return makeCallLayout();
+        }
+
+        @NonNull
+        private Action makeNegativeAction() {
+            if (mDeclineIntent == null) {
+                return makeAction(R.drawable.ic_call_decline,
+                        R.string.call_notification_hang_up_action,
+                        R.color.call_notification_decline_color, mHangUpIntent);
+            } else {
+                return makeAction(R.drawable.ic_call_decline,
+                        R.string.call_notification_decline_action,
+                        R.color.call_notification_decline_color, mDeclineIntent);
+            }
+        }
+
+        @Nullable
+        private Action makeAnswerAction() {
+            return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+                    R.string.call_notification_answer_action,
+                    R.color.call_notification_answer_color, mAnswerIntent);
+        }
+
+        @NonNull
+        private Action makeAction(@DrawableRes int icon, @StringRes int title,
+                @ColorRes int colorRes, PendingIntent intent) {
+            Action action = new Action.Builder(Icon.createWithResource("", icon),
+                    new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
+                            new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+                            SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
+                    intent).build();
+            action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
+            return action;
+        }
+
+        private ArrayList<Action> makeActionsList() {
+            final Action negativeAction = makeNegativeAction();
+            final Action answerAction = makeAnswerAction();
+
+            ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
+            final Action lastAction;
+            if (answerAction == null) {
+                // If there's no answer action, put the hang up / decline action at the end
+                lastAction = negativeAction;
+            } else {
+                // Otherwise put the answer action at the end, and put the decline action at start.
+                actions.add(negativeAction);
+                lastAction = answerAction;
+            }
+            // For consistency with the standard actions bar, contextual actions are ignored.
+            for (Action action : Builder.filterOutContextualActions(mBuilder.mActions)) {
+                if (actions.size() >= MAX_ACTION_BUTTONS - 1) {
+                    break;
+                }
+                actions.add(action);
+            }
+            actions.add(lastAction);
+            return actions;
+        }
+
+        private RemoteViews makeCallLayout() {
+            Bundle extras = mBuilder.mN.extras;
+            CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+            if (text == null) {
+                text = getDefaultText();
+            }
+
+            // Bind standard template
+            StandardTemplateParams p = mBuilder.mParams.reset()
+                    .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+                    .allowActionIcons(true)
+                    .hideLargeIcon(true)
+                    .text(text)
+                    .summaryText(mBuilder.processLegacyText(mVerificationText));
+            // TODO(b/179178086): hide the snooze button
+            RemoteViews contentView = mBuilder.applyStandardTemplate(
+                    mBuilder.getCallLayoutResource(), p, null /* result */);
+
+            // Bind actions.
+            mBuilder.resetStandardTemplateWithActions(contentView);
+            bindCallActions(contentView, p);
+
+            // Bind some extra conversation-specific header fields.
+            mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+            mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+            contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
+            bindCallerVerification(contentView, p);
+
+            // Bind some custom CallLayout properties
+            contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
+                    mBuilder.isColorized(p)
+                            ? mBuilder.getPrimaryTextColor(p)
+                            : mBuilder.resolveContrastColor(p));
+            contentView.setInt(R.id.status_bar_latest_event_content,
+                    "setNotificationBackgroundColor", mBuilder.resolveBackgroundColor(p));
+            contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
+                    mBuilder.mN.mLargeIcon);
+            contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
+                    mBuilder.mN.extras);
+
+            return contentView;
+        }
+
+        private void bindCallActions(RemoteViews view, StandardTemplateParams p) {
+            view.setViewVisibility(R.id.actions_container, View.VISIBLE);
+            view.setViewVisibility(R.id.actions, View.VISIBLE);
+            view.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                    RemoteViews.MARGIN_BOTTOM, 0);
+
+            // Clear view padding to allow buttons to start on the left edge.
+            // This must be done before 'setEmphasizedMode' which sets top/bottom margins.
+            view.setViewPadding(R.id.actions, 0, 0, 0, 0);
+            // Add an optional indent that will make buttons start at the correct column when
+            // there is enough space to do so (and fall back to the left edge if not).
+            view.setInt(R.id.actions, "setCollapsibleIndentDimen",
+                    R.dimen.call_notification_collapsible_indent);
+
+            // Emphasize so that buttons have borders or colored backgrounds
+            boolean emphasizedMode = true;
+            view.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
+            // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
+            // required actions (Answer, Decline, and Hang Up).
+            view.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
+
+            // Create the buttons for the generated actions list.
+            int i = 0;
+            for (Action action : makeActionsList()) {
+                final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
+                if (i > 0) {
+                    // Clear start margin from non-first buttons to reduce the gap between buttons.
+                    // (8dp remaining gap is from all buttons' standard 4dp inset).
+                    button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
+                }
+                view.addView(R.id.actions, button);
+                ++i;
+            }
+        }
+
+        private void bindCallerVerification(RemoteViews contentView, StandardTemplateParams p) {
+            if (mVerificationIcon != null) {
+                contentView.setImageViewIcon(R.id.verification_icon, mVerificationIcon);
+                contentView.setDrawableTint(R.id.verification_icon, false /* targetBackground */,
+                        mBuilder.getSecondaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
+                contentView.setViewVisibility(R.id.verification_icon, View.VISIBLE);
+            } else {
+                contentView.setViewVisibility(R.id.verification_icon, View.GONE);
+            }
+            if (!TextUtils.isEmpty(mVerificationText)) {
+                contentView.setTextViewText(R.id.verification_text, mVerificationText);
+                mBuilder.setTextViewColorSecondary(contentView, R.id.verification_text, p);
+                contentView.setViewVisibility(R.id.verification_text, View.VISIBLE);
+            } else {
+                contentView.setViewVisibility(R.id.verification_text, View.GONE);
+            }
+        }
+
+        @Nullable
+        private String getDefaultText() {
+            switch (mCallType) {
+                case CALL_TYPE_INCOMING:
+                    return mBuilder.mContext.getString(R.string.call_notification_incoming_text);
+                case CALL_TYPE_ONGOING:
+                    return mBuilder.mContext.getString(R.string.call_notification_ongoing_text);
+                case CALL_TYPE_SCREENING:
+                    return mBuilder.mContext.getString(R.string.call_notification_screening_text);
+            }
+            return null;
+        }
+
+        /**
+         * @hide
+         */
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+            extras.putInt(EXTRA_CALL_TYPE, mCallType);
+            extras.putParcelable(EXTRA_CALL_PERSON, mPerson);
+            if (mVerificationIcon != null) {
+                extras.putParcelable(EXTRA_VERIFICATION_ICON, mVerificationIcon);
+            }
+            if (mVerificationText != null) {
+                extras.putCharSequence(EXTRA_VERIFICATION_TEXT, mVerificationText);
+            }
+            if (mAnswerIntent != null) {
+                extras.putParcelable(EXTRA_ANSWER_INTENT, mAnswerIntent);
+            }
+            if (mDeclineIntent != null) {
+                extras.putParcelable(EXTRA_DECLINE_INTENT, mDeclineIntent);
+            }
+            if (mHangUpIntent != null) {
+                extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
+            }
+            fixTitleAndTextExtras(extras);
+        }
+
+        private void fixTitleAndTextExtras(Bundle extras) {
+            CharSequence sender = mPerson != null ? mPerson.getName() : null;
+            if (sender != null) {
+                extras.putCharSequence(EXTRA_TITLE, sender);
+            }
+            if (extras.getCharSequence(EXTRA_TEXT) == null) {
+                extras.putCharSequence(EXTRA_TEXT, getDefaultText());
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        protected void restoreFromExtras(Bundle extras) {
+            super.restoreFromExtras(extras);
+            mCallType = extras.getInt(EXTRA_CALL_TYPE);
+            mPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+            mVerificationIcon = extras.getParcelable(EXTRA_VERIFICATION_ICON);
+            mVerificationText = extras.getCharSequence(EXTRA_VERIFICATION_TEXT);
+            mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
+            mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
+            mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean hasSummaryInHeader() {
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            CallStyle otherS = (CallStyle) other;
+            return !Objects.equals(mCallType, otherS.mCallType)
+                    || !Objects.equals(mPerson, otherS.mPerson)
+                    || !Objects.equals(mVerificationText, otherS.mVerificationText);
+        }
+    }
+
     /**
      * Notification style for custom views that are decorated by the system
      *
@@ -11376,6 +11874,7 @@
         boolean mHideActions;
         boolean mHideProgress;
         boolean mPromotePicture;
+        boolean mAllowActionIcons;
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
@@ -11392,6 +11891,7 @@
             mHideActions = false;
             mHideProgress = false;
             mPromotePicture = false;
+            mAllowActionIcons = false;
             title = null;
             text = null;
             summaryText = null;
@@ -11431,6 +11931,11 @@
             return this;
         }
 
+        final StandardTemplateParams allowActionIcons(boolean allowActionIcons) {
+            this.mAllowActionIcons = allowActionIcons;
+            return this;
+        }
+
         final StandardTemplateParams promotePicture(boolean promotePicture) {
             this.mPromotePicture = promotePicture;
             return this;
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e6aa7a7..1ff64db 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -59,7 +59,7 @@
 per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
 
 # ResourcesManager
-per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
 
 # VoiceInteraction
 per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 0000000..21c39a8
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 772833cc..ac8d3a2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@
 import android.os.Process;
 import android.os.Trace;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
@@ -60,6 +61,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.WeakHashMap;
@@ -174,8 +176,8 @@
          * based on.
          *
          * @see #activityResources
-         * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
-         * CompatibilityInfo, ClassLoader, List)
+         * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+         * Configuration, CompatibilityInfo, ClassLoader, List)
          */
         public final Configuration overrideConfig = new Configuration();
 
@@ -482,8 +484,8 @@
             }
         }
 
-        if (key.mOverlayDirs != null) {
-            for (final String idmapPath : key.mOverlayDirs) {
+        if (key.mOverlayPaths != null) {
+            for (final String idmapPath : key.mOverlayPaths) {
                 apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
             }
         }
@@ -783,14 +785,16 @@
 
     /**
      * Creates base resources for a binder token. Calls to
-     * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
-     * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override
-     * configurations merged with the one specified here.
+     *
+     * {@link #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+     * Configuration, CompatibilityInfo, ClassLoader, List)} with the same binder token will have
+     * their override configurations merged with the one specified here.
      *
      * @param token Represents an {@link Activity} or {@link WindowContext}.
      * @param resDir The base resource path. Can be null (only framework resources will be loaded).
      * @param splitResDirs An array of split resource paths. Can be null.
-     * @param overlayDirs An array of overlay paths. Can be null.
+     * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+     * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
      * @param libDirs An array of resource library paths. Can be null.
      * @param displayId The ID of the display for which to create the resources.
      * @param overrideConfig The configuration to apply on top of the base configuration. Can be
@@ -804,7 +808,8 @@
     public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
             @Nullable String resDir,
             @Nullable String[] splitResDirs,
-            @Nullable String[] overlayDirs,
+            @Nullable String[] legacyOverlayDirs,
+            @Nullable String[] overlayPaths,
             @Nullable String[] libDirs,
             int displayId,
             @Nullable Configuration overrideConfig,
@@ -817,7 +822,7 @@
             final ResourcesKey key = new ResourcesKey(
                     resDir,
                     splitResDirs,
-                    overlayDirs,
+                    combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
                     libDirs,
                     displayId,
                     overrideConfig,
@@ -1043,7 +1048,8 @@
      * @param activityToken Represents an Activity. If null, global resources are assumed.
      * @param resDir The base resource path. Can be null (only framework resources will be loaded).
      * @param splitResDirs An array of split resource paths. Can be null.
-     * @param overlayDirs An array of overlay paths. Can be null.
+     * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+     * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
      * @param libDirs An array of resource library paths. Can be null.
      * @param overrideDisplayId The ID of the display for which the returned Resources should be
      * based. This will cause display-based configuration properties to override those of the base
@@ -1063,7 +1069,8 @@
             @Nullable IBinder activityToken,
             @Nullable String resDir,
             @Nullable String[] splitResDirs,
-            @Nullable String[] overlayDirs,
+            @Nullable String[] legacyOverlayDirs,
+            @Nullable String[] overlayPaths,
             @Nullable String[] libDirs,
             @Nullable Integer overrideDisplayId,
             @Nullable Configuration overrideConfig,
@@ -1075,7 +1082,7 @@
             final ResourcesKey key = new ResourcesKey(
                     resDir,
                     splitResDirs,
-                    overlayDirs,
+                    combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
                     libDirs,
                     overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
                     overrideConfig,
@@ -1250,7 +1257,7 @@
 
         // Create the new ResourcesKey with the rebased override config.
         final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
-                oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs,
+                oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
                 displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders);
 
         if (DEBUG) {
@@ -1393,7 +1400,7 @@
                         updatedResourceKeys.put(impl, new ResourcesKey(
                                 key.mResDir,
                                 key.mSplitResDirs,
-                                key.mOverlayDirs,
+                                key.mOverlayPaths,
                                 newLibAssets,
                                 key.mDisplayId,
                                 key.mOverrideConfiguration,
@@ -1423,7 +1430,8 @@
 
             // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
             String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
-            String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);
+            String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+                    appInfo.overlayPaths);
 
             final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
             final int implCount = mResourceImpls.size();
@@ -1458,6 +1466,39 @@
         }
     }
 
+    /**
+     * Creates an array with the contents of {@param overlayPaths} and the unique elements of
+     * {@param resourceDirs}.
+     *
+     * {@link ApplicationInfo#resourceDirs} only contains paths of overlays APKs.
+     * {@link ApplicationInfo#overlayPaths} was created to contain paths of overlay of varying file
+     * formats. It also contains the contents of {@code resourceDirs} because the order of loaded
+     * overlays matter. In case {@code resourceDirs} contains overlay APK paths that are not present
+     * in overlayPaths (perhaps an app inserted an additional overlay path into a
+     * {@code resourceDirs}), this method is used to combine the contents of {@code resourceDirs}
+     * that do not exist in {@code overlayPaths}} and {@code overlayPaths}}.
+     */
+    @Nullable
+    private static String[] combinedOverlayPaths(@Nullable String[] resourceDirs,
+            @Nullable String[] overlayPaths) {
+        if (resourceDirs == null) {
+            return ArrayUtils.cloneOrNull(overlayPaths);
+        } else if(overlayPaths == null) {
+            return ArrayUtils.cloneOrNull(resourceDirs);
+        } else {
+            final ArrayList<String> paths = new ArrayList<>();
+            for (final String path : overlayPaths) {
+                paths.add(path);
+            }
+            for (final String path : resourceDirs) {
+                if (!paths.contains(path)) {
+                    paths.add(path);
+                }
+            }
+            return paths.toArray(new String[0]);
+        }
+    }
+
     private void redirectResourcesToNewImplLocked(
             @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
         // Bail early if there is no work to do.
@@ -1559,7 +1600,7 @@
                 final ResourcesKey newKey = new ResourcesKey(
                         oldKey.mResDir,
                         oldKey.mSplitResDirs,
-                        oldKey.mOverlayDirs,
+                        oldKey.mOverlayPaths,
                         oldKey.mLibDirs,
                         oldKey.mDisplayId,
                         oldKey.mOverrideConfiguration,
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 9092ef36..29792ac 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -44,8 +44,8 @@
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
      * can only attach one {@link Context}.
      * <p>This method must be called before invoking
-     * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle,
-     * String)}.<p/>
+     * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
+     * Bundle, boolean)}.<p/>
      *
      * @param context context to be attached
      * @throws IllegalStateException if attached context has already existed.
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 8b0c706..9c07f85 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -57,6 +57,13 @@
     public abstract int getPermissionPolicy(@UserIdInt int userHandle);
 
     /**
+     * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the
+     * given user.
+     */
+    public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle);
+
+
+    /**
      * Empty implementation.
      */
     private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -77,5 +84,10 @@
         public int getPermissionPolicy(int userHandle) {
             return DevicePolicyManager.PERMISSION_POLICY_PROMPT;
         }
+
+        @Override
+        public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) {
+            return false;
+        }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 642bce4..ff41d1c8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -980,6 +980,19 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
 
     /**
+     * A boolean extra indicating the admin of a fully-managed device opts out of controlling
+     * permission grants for sensor-related permissions,
+     * see {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+     *
+     * The default for this extra is {@code false} - by default, the admin of a fully-managed
+     * device has the ability to grant sensors-related permissions.
+     *
+     * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only.
+     */
+    public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT =
+            "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
+
+    /**
      * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
      * android package archive at the download location specified in {@link
      * #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
@@ -1730,8 +1743,12 @@
      * Broadcast action to notify ManagedProvisioning that
      * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
      * @hide
+     * @deprecated No longer needed as ManagedProvisioning no longer handles
+     * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
      */
+    // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
             "android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
 
@@ -5469,26 +5486,6 @@
             "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
 
     /**
-     * Broadcast action: notify managed provisioning that the device has been provisioned.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
-            "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
-    /**
-     * Broadcast action: notify managed provisioning that a new managed profile is created.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MANAGED_PROFILE_CREATED =
-            "android.app.action.MANAGED_PROFILE_CREATED";
-
-    /**
      * Widgets are enabled in keyguard
      */
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -10536,6 +10533,13 @@
      * As this policy only acts on runtime permission requests, it only applies to applications
      * built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later.
      *
+     * <p>
+     * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, an auto-grant
+     * policy will not apply to certain sensors-related permissions on some configurations.
+     * See {@link #setPermissionGrantState(ComponentName, String, String, int)} for the list of
+     * permissions affected, and the behavior change for managed profiles and fully-managed
+     * devices.
+     *
      * @param admin Which profile or device owner this request is associated with.
      * @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT},
      *            {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
@@ -10594,6 +10598,31 @@
      * application built with a {@code targetSdkVersion} &lt;
      * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
      * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
+     * <p>
+     * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
+     * the following, sensors-related, permissions is restricted:
+     * <ul>
+     *    <li>Manifest.permission.ACCESS_FINE_LOCATION</li>
+     *    <li>Manifest.permission.ACCESS_BACKGROUND_LOCATION</li>
+     *    <li>Manifest.permission.ACCESS_COARSE_LOCATION</li>
+     *    <li>Manifest.permission.CAMERA</li>
+     *    <li>Manifest.permission.RECORD_AUDIO</li>
+     *    <li>Manifest.permission.RECORD_BACKGROUND_AUDIO</li>
+     *    <li>Manifest.permission.ACTIVITY_RECOGNITION</li>
+     *    <li>Manifest.permission.BODY_SENSORS</li>
+     * </ul>
+     * <p>
+     * A profile owner may not grant these permissions (i.e. call this method with any of the
+     * permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}),
+     * but may deny them.
+     * <p>
+     * A device owner, by default, may continue granting these permissions. However, for increased
+     * user control, the admin may opt out of controlling grants for these permissions by including
+     * {@link #EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters. In that
+     * case the device owner's control will be limited do denying these permissions.
+     * <p>
+     * Attempts by the admin to grant these permissions, when the admin is restricted from doing
+     * so, will be silently ignored (no exception will be thrown).
      *
      * @param admin Which profile or device owner this request is associated with.
      * @param packageName The application to grant or revoke a permission to.
@@ -13268,4 +13297,58 @@
             }
         }
     }
+
+    /**
+     * Resets the default cross profile intent filters that were set during
+     * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+     * profiles if any.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        if (mService != null) {
+            try {
+                mService.resetDefaultCrossProfileIntentFilters(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
+    /**
+     * Returns true if the caller is running on a device where the admin can grant
+     * permissions related to device sensors.
+     * This is a signal that the device is a fully-managed device where personal usage is
+     * discouraged.
+     * The list of permissions is listed in
+     * {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+     *
+     * May be called by any app.
+     * @return true if the app can grant device sensors-related permissions, false otherwise.
+     */
+    public boolean canAdminGrantSensorsPermissions() {
+        throwIfParentInstance("canAdminGrantSensorsPermissions");
+        return canAdminGrantSensorsPermissionsForUser(myUserId());
+    }
+
+    /**
+     * Returns true if the admin can control grants of sensors-related permissions, for
+     * a given user.
+     *
+     * @hide
+     * @param userId The ID of the user to check.
+     * @return if the admin may grant these permissions, false otherwise.
+     */
+    @SystemApi
+    public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+        if (mService == null) {
+            return false;
+        }
+        try {
+            return mService.canAdminGrantSensorsPermissionsForUser(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 83af019..5e1cbad 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -42,6 +42,7 @@
     private final long mLocalTime;
     @SuppressLint("UseIcu")
     @Nullable private final Locale mLocale;
+    private final boolean mDeviceOwnerCanGrantSensorsPermissions;
 
     private FullyManagedDeviceProvisioningParams(
             @NonNull ComponentName deviceAdminComponentName,
@@ -49,13 +50,16 @@
             boolean leaveAllSystemAppsEnabled,
             @Nullable String timeZone,
             long localTime,
-            @Nullable @SuppressLint("UseIcu") Locale locale) {
+            @Nullable @SuppressLint("UseIcu") Locale locale,
+            boolean deviceOwnerCanGrantSensorsPermissions) {
         this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
         this.mOwnerName = requireNonNull(ownerName);
         this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
         this.mTimeZone = timeZone;
         this.mLocalTime = localTime;
         this.mLocale = locale;
+        this.mDeviceOwnerCanGrantSensorsPermissions =
+                deviceOwnerCanGrantSensorsPermissions;
     }
 
     private FullyManagedDeviceProvisioningParams(
@@ -64,13 +68,15 @@
             boolean leaveAllSystemAppsEnabled,
             @Nullable String timeZone,
             long localTime,
-            @Nullable String localeStr) {
+            @Nullable String localeStr,
+            boolean deviceOwnerCanGrantSensorsPermissions) {
         this(deviceAdminComponentName,
                 ownerName,
                 leaveAllSystemAppsEnabled,
                 timeZone,
                 localTime,
-                getLocale(localeStr));
+                getLocale(localeStr),
+                deviceOwnerCanGrantSensorsPermissions);
     }
 
     @Nullable
@@ -107,6 +113,14 @@
     }
 
     /**
+     * @return true if the device owner can control sensor-related permission grants, false
+     * if the device owner has opted out of it.
+     */
+    public boolean canDeviceOwnerGrantSensorsPermissions() {
+        return mDeviceOwnerCanGrantSensorsPermissions;
+    }
+
+    /**
      * Builder class for {@link FullyManagedDeviceProvisioningParams} objects.
      */
     public static final class Builder {
@@ -117,6 +131,8 @@
         private long mLocalTime;
         @SuppressLint("UseIcu")
         @Nullable private Locale mLocale;
+        // Default to allowing control over sensor permission grants.
+        boolean mDeviceOwnerCanGrantSensorsPermissions = true;
 
         /**
          * Initialize a new {@link Builder} to construct a
@@ -181,6 +197,17 @@
         }
 
         /**
+         * Marks that the Device Owner may grant permissions related to device sensors.
+         * See {@link DevicePolicyManager#EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT}.
+         */
+        @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+            mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
+            return this;
+        }
+
+        /**
          * Combines all of the attributes that have been set on this {@code Builder}
          *
          * @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -193,7 +220,8 @@
                     mLeaveAllSystemAppsEnabled,
                     mTimeZone,
                     mLocalTime,
-                    mLocale);
+                    mLocale,
+                    mDeviceOwnerCanGrantSensorsPermissions);
         }
     }
 
@@ -211,6 +239,8 @@
                 + ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone)
                 + ", mLocalTime=" + mLocalTime
                 + ", mLocale=" + (mLocale == null ? "null" : mLocale)
+                + ", mDeviceOwnerCanGrantSensorsPermissions="
+                + mDeviceOwnerCanGrantSensorsPermissions
                 + '}';
     }
 
@@ -222,6 +252,7 @@
         dest.writeString(mTimeZone);
         dest.writeLong(mLocalTime);
         dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
+        dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
     }
 
     @NonNull
@@ -235,6 +266,7 @@
                     String timeZone = in.readString();
                     long localtime = in.readLong();
                     String locale = in.readString();
+                    boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
 
                     return new FullyManagedDeviceProvisioningParams(
                             componentName,
@@ -242,7 +274,8 @@
                             leaveAllSystemAppsEnabled,
                             timeZone,
                             localtime,
-                            locale);
+                            locale,
+                            deviceOwnerCanGrantSensorsPermissions);
                 }
 
                 @Override
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f9ee153..89f30cc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -498,4 +498,7 @@
 
     UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
     void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+    void resetDefaultCrossProfileIntentFilters(int userId);
+    boolean canAdminGrantSensorsPermissionsForUser(int userId);
 }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -460,24 +461,40 @@
                                                    Set<PathWithRequiredFlags> excludes,
                                                    Map<String, Set<PathWithRequiredFlags>> includes)
                 throws IOException, XmlPullParserException {
+            verifyTopLevelTag(parser, "full-backup-content");
+
+            parseRules(parser, excludes, includes, Optional.empty());
+
+            logParsingResults(excludes, includes);
+        }
+
+        private void verifyTopLevelTag(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException {
             int event = parser.getEventType(); // START_DOCUMENT
             while (event != XmlPullParser.START_TAG) {
                 event = parser.next();
             }
 
-            if (!"full-backup-content".equals(parser.getName())) {
+            if (!tag.equals(parser.getName())) {
                 throw new XmlPullParserException("Xml file didn't start with correct tag" +
-                        " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+                        " (" + tag + " ). Found \"" + parser.getName() + "\"");
             }
 
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "====================================================");
-                Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+                Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
                 Log.v(TAG_XML_PARSER, "====================================================");
                 Log.v(TAG_XML_PARSER, "");
             }
+        }
 
+        private void parseRules(XmlPullParser parser,
+                Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes,
+                Optional<Integer> maybeRequiredFlags)
+                throws IOException, XmlPullParserException {
+            int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 switch (event) {
                     case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
                             break;
                         }
 
-                        int requiredFlags = 0; // no transport flags are required by default
-                        if (TAG_INCLUDE.equals(parser.getName())) {
-                            // requiredFlags are only supported for <include /> tag, for <exclude />
-                            // we should always leave them as the default = 0
-                            requiredFlags = getRequiredFlagsFromString(
-                                    parser.getAttributeValue(null, "requireFlags"));
-                        }
+                        int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
 
                         // retrieve the include/exclude set we'll be adding this rule to
                         Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
 
                         // Special case for sharedpref files (not dirs) also add ".xml" suffix file.
                         if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
-                            !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+                                !canonicalFile.getCanonicalPath().endsWith(".xml")) {
                             final String canonicalXmlPath =
                                     canonicalFile.getCanonicalPath() + ".xml";
                             activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
                         }
                 }
             }
+        }
+
+        private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes) {
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
             return flags;
         }
 
+        private int getRequiredFlagsForRule(XmlPullParser parser,
+                Optional<Integer> maybeRequiredFlags) {
+            if (maybeRequiredFlags.isPresent()) {
+                // This is the new config format where required flags are specified for the whole
+                // section, not per rule.
+                return maybeRequiredFlags.get();
+            }
+
+            if (TAG_INCLUDE.equals(parser.getName())) {
+                // In the legacy config, requiredFlags are only supported for <include /> tag,
+                // for <exclude /> we should always leave them as the default = 0.
+                return getRequiredFlagsFromString(
+                        parser.getAttributeValue(null, "requireFlags"));
+            }
+
+            return 0;
+        }
+
         private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
                 Set<PathWithRequiredFlags> excludes,
                 Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
index 1e0653d..920b9fe 100644
--- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -138,6 +138,15 @@
         dest.writeInt(mEventType);
     }
 
+    @Override
+    public String toString() {
+        return "SmartspaceTargetEvent{"
+                + "mSmartspaceTarget=" + mSmartspaceTarget
+                + ", mSmartspaceActionId='" + mSmartspaceActionId + '\''
+                + ", mEventType=" + mEventType
+                + '}';
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 2c1e951..30ea5c4 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,7 +30,7 @@
 interface IUsageStatsManager {
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
-            String callingPackage);
+            String callingPackage, int userId);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
             String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f74d16e..31781ec 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -437,11 +438,12 @@
      * @see #INTERVAL_YEARLY
      * @see #INTERVAL_BEST
      */
+    @UserHandleAware
     public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
         try {
             @SuppressWarnings("unchecked")
             ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
-                    endTime, mContext.getOpPackageName());
+                    endTime, mContext.getOpPackageName(), mContext.getUserId());
             if (slice != null) {
                 return slice.getList();
             }
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index e96e22c4..42214d0 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -247,12 +247,29 @@
      * A preview of what the AppWidget will look like after it's configured.
      * If not supplied, the AppWidget's icon will be used.
      *
-     * <p>This field corresponds to the <code>android:previewImage</code> attribute in
-     * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * <p>This field corresponds to the <code>android:previewImage</code> attribute in the AppWidget
+     * meta-data file.
      */
     public int previewImage;
 
     /**
+     * The layout resource id of a preview of what the AppWidget will look like after it's
+     * configured.
+     *
+     * <p>Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+     * system themes, display sizes & density etc.
+     *
+     * <p>If supplied, this will take precedence over the previewImage on supported widget hosts.
+     * Otherwise, previewImage will be used.
+     *
+     * <p>This field corresponds to the <code>android:previewLayout</code> attribute in the
+     * AppWidget meta-data file.
+     */
+    @SuppressLint("MutableBareField")
+    @IdRes
+    public int previewLayout;
+
+    /**
      * The rules by which a widget can be resized. See {@link #RESIZE_NONE},
      * {@link #RESIZE_NONE}, {@link #RESIZE_HORIZONTAL},
      * {@link #RESIZE_VERTICAL}, {@link #RESIZE_BOTH}.
@@ -320,6 +337,7 @@
         this.label = in.readString();
         this.icon = in.readInt();
         this.previewImage = in.readInt();
+        this.previewLayout = in.readInt();
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
         this.widgetCategory = in.readInt();
@@ -429,6 +447,7 @@
         out.writeString(this.label);
         out.writeInt(this.icon);
         out.writeInt(this.previewImage);
+        out.writeInt(this.previewLayout);
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
         out.writeInt(this.widgetCategory);
@@ -453,6 +472,7 @@
         that.label = this.label;
         that.icon = this.icon;
         that.previewImage = this.previewImage;
+        that.previewLayout = this.previewLayout;
         that.autoAdvanceViewId = this.autoAdvanceViewId;
         that.resizeMode = this.resizeMode;
         that.widgetCategory = this.widgetCategory;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 7eda50e..ea7e5ea 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3198,6 +3198,61 @@
     }
 
     /**
+     * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+     * e.g. in the event the bluetooth is turned off/on via settings.
+     *
+     * If the bluetooth stack is currently up, there will not be an initial callback call.
+     * You can use the return value as an indication of this being the case.
+     *
+     * Callbacks will be delivered on a binder thread.
+     *
+     * @return whether bluetooth is already up currently
+     *
+     * @hide
+     */
+    public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        return getBluetoothService(callback.mRemote) != null;
+    }
+
+    /**
+     * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        removeServiceStateCallback(callback.mRemote);
+    }
+
+    /**
+     * A callback for {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public abstract static class ServiceLifecycleCallback {
+
+        /** Called when the bluetooth stack is up */
+        public abstract void onBluetoothServiceUp();
+
+        /** Called when the bluetooth stack is down */
+        public abstract void onBluetoothServiceDown();
+
+        IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+            @Override
+            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                ServiceLifecycleCallback.this.onBluetoothServiceUp();
+            }
+
+            @Override
+            public void onBluetoothServiceDown() {
+                ServiceLifecycleCallback.this.onBluetoothServiceDown();
+            }
+
+            @Override
+            public void onBrEdrDown() {}
+        };
+    }
+
+    /**
      * Starts a scan for Bluetooth LE devices.
      *
      * <p>Results of the scan are reported using the
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index c0736a6..d82cf19 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -167,6 +167,11 @@
     /** @hide */
     @NonNull
     @SystemApi
+    public static final ParcelUuid VOLUME_CONTROL =
+            ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 17bf11b..9007d9d 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -23,6 +23,7 @@
 
 import com.android.internal.util.DataClass;
 
+import java.util.Date;
 import java.util.Objects;
 
 /**
@@ -38,16 +39,23 @@
     private final @NonNull String mDeviceMacAddress;
     private final @NonNull String mPackageName;
     private final @Nullable String mDeviceProfile;
-    private final boolean mKeepProfilePrivilegesWhenDeviceAway;
+    private final boolean mNotifyOnDeviceNearby;
+    private final long mTimeApprovedMs;
 
     /** @hide */
     public int getUserId() {
         return mUserId;
     }
 
+    private String timeApprovedMsToString() {
+        return new Date(mTimeApprovedMs).toString();
+    }
 
 
-    // Code below generated by codegen v1.0.21.
+
+
+
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -71,7 +79,8 @@
             @NonNull String deviceMacAddress,
             @NonNull String packageName,
             @Nullable String deviceProfile,
-            boolean keepProfilePrivilegesWhenDeviceAway) {
+            boolean notifyOnDeviceNearby,
+            long timeApprovedMs) {
         this.mUserId = userId;
         com.android.internal.util.AnnotationValidations.validate(
                 UserIdInt.class, null, mUserId);
@@ -82,7 +91,8 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+        this.mTimeApprovedMs = timeApprovedMs;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -103,8 +113,13 @@
     }
 
     @DataClass.Generated.Member
-    public boolean isKeepProfilePrivilegesWhenDeviceAway() {
-        return mKeepProfilePrivilegesWhenDeviceAway;
+    public boolean isNotifyOnDeviceNearby() {
+        return mNotifyOnDeviceNearby;
+    }
+
+    @DataClass.Generated.Member
+    public long getTimeApprovedMs() {
+        return mTimeApprovedMs;
     }
 
     @Override
@@ -118,7 +133,8 @@
                 "deviceMacAddress = " + mDeviceMacAddress + ", " +
                 "packageName = " + mPackageName + ", " +
                 "deviceProfile = " + mDeviceProfile + ", " +
-                "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
+                "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " +
+                "timeApprovedMs = " + timeApprovedMsToString() +
         " }";
     }
 
@@ -139,7 +155,8 @@
                 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                 && Objects.equals(mPackageName, that.mPackageName)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
-                && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
+                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+                && mTimeApprovedMs == that.mTimeApprovedMs;
     }
 
     @Override
@@ -153,7 +170,8 @@
         _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
         _hash = 31 * _hash + Objects.hashCode(mPackageName);
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
-        _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
+        _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
+        _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs);
         return _hash;
     }
 
@@ -164,13 +182,14 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
+        if (mNotifyOnDeviceNearby) flg |= 0x10;
         if (mDeviceProfile != null) flg |= 0x8;
         dest.writeByte(flg);
         dest.writeInt(mUserId);
         dest.writeString(mDeviceMacAddress);
         dest.writeString(mPackageName);
         if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+        dest.writeLong(mTimeApprovedMs);
     }
 
     @Override
@@ -185,11 +204,12 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
+        boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
         int userId = in.readInt();
         String deviceMacAddress = in.readString();
         String packageName = in.readString();
         String deviceProfile = (flg & 0x8) == 0 ? null : in.readString();
+        long timeApprovedMs = in.readLong();
 
         this.mUserId = userId;
         com.android.internal.util.AnnotationValidations.validate(
@@ -201,7 +221,8 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+        this.mTimeApprovedMs = timeApprovedMs;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -221,10 +242,10 @@
     };
 
     @DataClass.Generated(
-            time = 1606940835778L,
-            codegenVersion = "1.0.21",
+            time = 1612832377589L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/Association.java",
-            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mKeepProfilePrivilegesWhenDeviceAway\npublic  int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mNotifyOnDeviceNearby\nprivate final  long mTimeApprovedMs\npublic  int getUserId()\nprivate  java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 527d8df..95d3515 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -49,4 +49,6 @@
     void registerDevicePresenceListenerService(in String packageName, in String deviceAddress);
 
     void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
+
+    boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5d28216..2a402b2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3552,6 +3552,7 @@
             //@hide: NETWORK_SCORE_SERVICE,
             USAGE_STATS_SERVICE,
             MEDIA_SESSION_SERVICE,
+            MEDIA_COMMUNICATION_SERVICE,
             BATTERY_SERVICE,
             JOB_SCHEDULER_SERVICE,
             //@hide: PERSISTENT_DATA_BLOCK_SERVICE,
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6ec1169..01ff432 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -923,6 +923,14 @@
     public String[] resourceDirs;
 
     /**
+     * Contains the contents of {@link #resourceDirs} and along with paths for overlays that may or
+     * may not be APK packages.
+     *
+     * {@hide}
+     */
+    public String[] overlayPaths;
+
+    /**
      * String retrieved from the seinfo tag found in selinux policy. This value can be set through
      * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
      * context on the process as well as its data directory.
@@ -1472,6 +1480,9 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
         }
+        if (overlayPaths != null) {
+            pw.println(prefix + "overlayPaths=" + Arrays.toString(overlayPaths));
+        }
         if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
             pw.println(prefix + "seinfo=" + seInfo);
             pw.println(prefix + "seinfoUser=" + seInfoUser);
@@ -1568,6 +1579,11 @@
                 proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
             }
         }
+        if (overlayPaths != null) {
+            for (String dir : overlayPaths) {
+                proto.write(ApplicationInfoProto.OVERLAY_PATHS, dir);
+            }
+        }
         proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
         proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
         if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
@@ -1717,6 +1733,7 @@
         primaryCpuAbi = orig.primaryCpuAbi;
         secondaryCpuAbi = orig.secondaryCpuAbi;
         resourceDirs = orig.resourceDirs;
+        overlayPaths = orig.overlayPaths;
         seInfo = orig.seInfo;
         seInfoUser = orig.seInfoUser;
         sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -1803,6 +1820,7 @@
         dest.writeString8(primaryCpuAbi);
         dest.writeString8(secondaryCpuAbi);
         dest.writeString8Array(resourceDirs);
+        dest.writeString8Array(overlayPaths);
         dest.writeString8(seInfo);
         dest.writeString8(seInfoUser);
         dest.writeString8Array(sharedLibraryFiles);
@@ -1886,6 +1904,7 @@
         primaryCpuAbi = source.readString8();
         secondaryCpuAbi = source.readString8();
         resourceDirs = source.createString8Array();
+        overlayPaths = source.createString8Array();
         seInfo = source.readString8();
         seInfoUser = source.readString8();
         sharedLibraryFiles = source.createString8Array();
@@ -2282,7 +2301,9 @@
      * @hide
      */
     public String[] getAllApkPaths() {
-        final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs };
+        final String[][] inputLists = {
+                splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
+        };
         final List<String> output = new ArrayList<>(10);
         if (sourceDir != null) {
             output.add(sourceDir);
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
similarity index 64%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/content/pm/IOnChecksumsReadyListener.aidl
index e0eb06cb..7963ce1 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
+package android.content.pm;
+
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+    void onChecksumsReady(in List<ApkChecksum> checksums);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b8829bb..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
-    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
 
     //------------------------------------------------------------------------
     //
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6292575..0c0e402 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -450,6 +450,7 @@
                 FLAG_MATCH_PINNED,
                 FLAG_MATCH_MANIFEST,
                 FLAG_MATCH_CACHED,
+                FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
                 FLAG_GET_KEY_FIELDS_ONLY,
                 FLAG_GET_PERSONS_DATA,
         })
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d09d83f..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3858,13 +3858,6 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
-    /**
-     * Extra field name for the ID of a package pending verification. Passed to
-     * a package verifier and is used to call back to
-     * @see #requestChecksums
-     */
-    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@
      */
     public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
 
+    /** Listener that gets notified when checksums are available. */
+    @FunctionalInterface
+    public interface OnChecksumsReadyListener {
+        /**
+         * Called when the checksums are available.
+         *
+         * @param checksums array of checksums.
+         */
+        void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+    }
+
     /**
      * Requesting the checksums for APKs within a package.
-     * The checksums will be returned asynchronously via statusReceiver.
+     * The checksums will be returned asynchronously via onChecksumsReadyListener.
      *
      * By default returns all readily available checksums:
      * - enforced by platform,
@@ -8730,15 +8734,14 @@
      *                          {@link #TRUST_ALL} will return checksums from any installer,
      *                          {@link #TRUST_NONE} disables optimized installer-enforced checksums,
      *                          otherwise the list has to be non-empty list of certificates.
-     * @param statusReceiver called once when the results are available as
-     *                       {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+     * @param onChecksumsReadyListener called once when the results are available.
      * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
      * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
      * @throws NameNotFoundException if a package with the given name cannot be found on the system.
      */
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e6c0f6a..0819d17 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.split.SplitAssetLoader;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
@@ -7969,7 +7970,11 @@
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
         ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
-        ai.resourceDirs = state.getAllOverlayPaths();
+        final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+        if (overlayPaths != null) {
+            ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+            ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+        }
         ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
     }
 
@@ -8600,6 +8605,7 @@
                 null,
                 null,
                 androidAppInfo.resourceDirs,
+                androidAppInfo.overlayPaths,
                 androidAppInfo.sharedLibraryFiles,
                 null,
                 null,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 5cc74c0..e115597 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.os.BaseBundle;
 import android.os.Debug;
@@ -53,7 +54,6 @@
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Objects;
 
@@ -85,9 +85,10 @@
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
-    private String[] overlayPaths;
-    private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
-    private String[] cachedOverlayPaths;
+    private OverlayPaths overlayPaths;
+    // Maps library name to overlay paths.
+    private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths;
+    private OverlayPaths cachedOverlayPaths;
 
     @Nullable
     private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
@@ -121,8 +122,7 @@
         uninstallReason = o.uninstallReason;
         disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
         enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
-        overlayPaths =
-            o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+        overlayPaths = o.overlayPaths;
         if (o.sharedLibraryOverlayPaths != null) {
             sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
         }
@@ -132,25 +132,55 @@
         }
     }
 
-    public String[] getOverlayPaths() {
+    @Nullable
+    public OverlayPaths getOverlayPaths() {
         return overlayPaths;
     }
 
-    public void setOverlayPaths(String[] paths) {
-        overlayPaths = paths;
-        cachedOverlayPaths = null;
-    }
-
-    public Map<String, String[]> getSharedLibraryOverlayPaths() {
+    @Nullable
+    public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
         return sharedLibraryOverlayPaths;
     }
 
-    public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+    /**
+     * Sets the path of overlays currently enabled for this package and user combination.
+     * @return true if the path contents differ than what they were previously
+     */
+    @Nullable
+    public boolean setOverlayPaths(@Nullable OverlayPaths paths) {
+        if (Objects.equals(paths, overlayPaths)) {
+            return false;
+        }
+        if ((overlayPaths == null && paths.isEmpty())
+                || (paths == null && overlayPaths.isEmpty())) {
+            return false;
+        }
+        overlayPaths = paths;
+        cachedOverlayPaths = null;
+        return true;
+    }
+
+    /**
+     * Sets the path of overlays currently enabled for a library that this package uses.
+     *
+     * @return true if the path contents for the library differ than what they were previously
+     */
+    public boolean setSharedLibraryOverlayPaths(@NonNull String library,
+            @Nullable OverlayPaths paths) {
         if (sharedLibraryOverlayPaths == null) {
             sharedLibraryOverlayPaths = new ArrayMap<>();
         }
-        sharedLibraryOverlayPaths.put(library, paths);
+        final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library);
+        if (Objects.equals(paths, currentPaths)) {
+            return false;
+        }
         cachedOverlayPaths = null;
+        if (paths == null || paths.isEmpty()) {
+            return sharedLibraryOverlayPaths.remove(library) != null;
+        } else {
+            sharedLibraryOverlayPaths.put(library, paths);
+            return true;
+        }
     }
 
     /**
@@ -332,35 +362,21 @@
         return isComponentEnabled;
     }
 
-    public String[] getAllOverlayPaths() {
+    public OverlayPaths getAllOverlayPaths() {
         if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
             return null;
         }
-
         if (cachedOverlayPaths != null) {
             return cachedOverlayPaths;
         }
-
-        final LinkedHashSet<String> paths = new LinkedHashSet<>();
-        if (overlayPaths != null) {
-            final int N = overlayPaths.length;
-            for (int i = 0; i < N; i++) {
-                paths.add(overlayPaths[i]);
-            }
-        }
-
+        final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
+        newPaths.addAll(overlayPaths);
         if (sharedLibraryOverlayPaths != null) {
-            for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
-                if (libOverlayPaths != null) {
-                    final int N = libOverlayPaths.length;
-                    for (int i = 0; i < N; i++) {
-                        paths.add(libOverlayPaths[i]);
-                    }
-                }
+            for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+                newPaths.addAll(libOverlayPaths);
             }
         }
-
-        cachedOverlayPaths = paths.toArray(new String[0]);
+        cachedOverlayPaths = newPaths.build();
         return cachedOverlayPaths;
     }
 
diff --git a/core/java/android/content/pm/overlay/OverlayPaths.java b/core/java/android/content/pm/overlay/OverlayPaths.java
new file mode 100644
index 0000000..a4db733
--- /dev/null
+++ b/core/java/android/content/pm/overlay/OverlayPaths.java
@@ -0,0 +1,192 @@
+/*
+ * 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.content.pm.overlay;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
+        genEqualsHashCode = true, genToString = true)
+public class OverlayPaths {
+    /**
+     * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+     * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+     * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+     * path.
+     */
+    @NonNull
+    private final List<String> mResourceDirs = new ArrayList<>();
+
+    /**
+     * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+     * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+     * that are not APKs.
+     */
+    @NonNull
+    private final List<String> mOverlayPaths = new ArrayList<>();
+
+    public static class Builder {
+        final OverlayPaths mPaths = new OverlayPaths();
+
+        /**
+         * Adds a non-APK path to the contents of {@link OverlayPaths#getOverlayPaths()}.
+         */
+        public Builder addNonApkPath(@NonNull String idmapPath) {
+            mPaths.mOverlayPaths.add(idmapPath);
+            return this;
+        }
+
+        /**
+         * Adds a overlay APK path to the contents of {@link OverlayPaths#getResourceDirs()} and
+         * {@link OverlayPaths#getOverlayPaths()}.
+         */
+        public Builder addApkPath(@NonNull String overlayPath) {
+            addUniquePath(mPaths.mResourceDirs, overlayPath);
+            addUniquePath(mPaths.mOverlayPaths, overlayPath);
+            return this;
+        }
+
+        public Builder addAll(@Nullable OverlayPaths other) {
+            if (other != null) {
+                for (final String path : other.getResourceDirs()) {
+                    addUniquePath(mPaths.mResourceDirs, path);
+                }
+                for (final String path : other.getOverlayPaths()) {
+                    addUniquePath(mPaths.mOverlayPaths, path);
+                }
+            }
+            return this;
+        }
+
+        public OverlayPaths build() {
+            return mPaths;
+        }
+
+        private static void addUniquePath(@NonNull List<String> paths, @NonNull String path) {
+            if (!paths.contains(path)) {
+                paths.add(path);
+            }
+        }
+    }
+
+    /**
+     * Returns whether {@link #getOverlayPaths()} and {@link #getOverlayPaths} are empty.
+     */
+    public boolean isEmpty() {
+        return mResourceDirs.isEmpty() && mOverlayPaths.isEmpty();
+    }
+
+    private OverlayPaths() {
+    }
+
+
+
+    // 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/content/pm/overlay/OverlayPaths.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+     * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+     * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+     * path.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getResourceDirs() {
+        return mResourceDirs;
+    }
+
+    /**
+     * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+     * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+     * that are not APKs.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getOverlayPaths() {
+        return mOverlayPaths;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "OverlayPaths { " +
+                "resourceDirs = " + mResourceDirs + ", " +
+                "overlayPaths = " + mOverlayPaths +
+                " }";
+    }
+
+    @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(OverlayPaths other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        OverlayPaths that = (OverlayPaths) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(mResourceDirs, that.mResourceDirs)
+                && Objects.equals(mOverlayPaths, that.mOverlayPaths);
+    }
+
+    @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 + Objects.hashCode(mResourceDirs);
+        _hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1612307813586L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java",
+            inputSignatures = "private final @android.annotation.NonNull java.util.List<java.lang.String> mResourceDirs\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mOverlayPaths\npublic  boolean isEmpty()\nclass OverlayPaths extends java.lang.Object implements []\nfinal  android.content.pm.overlay.OverlayPaths mPaths\npublic  android.content.pm.overlay.OverlayPaths.Builder addNonApkPath(java.lang.String)\npublic  android.content.pm.overlay.OverlayPaths.Builder addApkPath(java.lang.String)\npublic  android.content.pm.overlay.OverlayPaths.Builder addAll(android.content.pm.overlay.OverlayPaths)\npublic  android.content.pm.overlay.OverlayPaths build()\nprivate static  void addUniquePath(java.util.List<java.lang.String>,java.lang.String)\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b7365b3..fb0d904 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -41,6 +41,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
@@ -412,7 +413,11 @@
             ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
         }
         ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
-        ai.resourceDirs = state.getAllOverlayPaths();
+        final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+        if (overlayPaths != null) {
+            ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+            ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+        }
 
         return ai;
     }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index cbd2c55..9012b5ce 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -108,17 +108,14 @@
 
         permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
 
-        if (permission.getProtectionFlags() != 0) {
-            if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
-                    == 0
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
-                    != PermissionInfo.PROTECTION_SIGNATURE
-                    && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
-                    != PermissionInfo.PROTECTION_INTERNAL) {
-                return input.error("<permission>  protectionLevel specifies a non-instant flag "
-                        + "but is not based on signature or internal type");
-            }
+        final int otherProtectionFlags = permission.getProtectionFlags()
+                & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+                | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+        if (otherProtectionFlags != 0
+                && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
+                && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+            return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+                    + " non-runtimeOnly flag but is not based on signature or internal type");
         }
 
         return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 05769dd..99b56a8 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -38,7 +38,7 @@
     public final String[] mSplitResDirs;
 
     @Nullable
-    public final String[] mOverlayDirs;
+    public final String[] mOverlayPaths;
 
     @Nullable
     public final String[] mLibDirs;
@@ -67,7 +67,7 @@
 
     public ResourcesKey(@Nullable String resDir,
                         @Nullable String[] splitResDirs,
-                        @Nullable String[] overlayDirs,
+                        @Nullable String[] overlayPaths,
                         @Nullable String[] libDirs,
                         int overrideDisplayId,
                         @Nullable Configuration overrideConfig,
@@ -75,7 +75,7 @@
                         @Nullable ResourcesLoader[] loader) {
         mResDir = resDir;
         mSplitResDirs = splitResDirs;
-        mOverlayDirs = overlayDirs;
+        mOverlayPaths = overlayPaths;
         mLibDirs = libDirs;
         mLoaders = (loader != null && loader.length == 0) ? null : loader;
         mDisplayId = overrideDisplayId;
@@ -86,7 +86,7 @@
         int hash = 17;
         hash = 31 * hash + Objects.hashCode(mResDir);
         hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
-        hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
+        hash = 31 * hash + Arrays.hashCode(mOverlayPaths);
         hash = 31 * hash + Arrays.hashCode(mLibDirs);
         hash = 31 * hash + Objects.hashCode(mDisplayId);
         hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
@@ -98,12 +98,12 @@
     @UnsupportedAppUsage
     public ResourcesKey(@Nullable String resDir,
             @Nullable String[] splitResDirs,
-            @Nullable String[] overlayDirs,
+            @Nullable String[] overlayPaths,
             @Nullable String[] libDirs,
             int displayId,
             @Nullable Configuration overrideConfig,
             @Nullable CompatibilityInfo compatInfo) {
-        this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+        this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo,
                 null);
     }
 
@@ -115,7 +115,7 @@
         if (mResDir != null && mResDir.startsWith(path)) {
             return true;
         } else {
-            return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path)
+            return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayPaths, path)
                     || anyStartsWith(mLibDirs, path);
         }
     }
@@ -154,7 +154,7 @@
         if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
             return false;
         }
-        if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) {
+        if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) {
             return false;
         }
         if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
@@ -186,8 +186,8 @@
         }
         builder.append("]");
         builder.append(" mOverlayDirs=[");
-        if (mOverlayDirs != null) {
-            builder.append(TextUtils.join(",", mOverlayDirs));
+        if (mOverlayPaths != null) {
+            builder.append(TextUtils.join(",", mOverlayPaths));
         }
         builder.append("]");
         builder.append(" mLibDirs=[");
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 4145a72..08b1e24 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -29,7 +29,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.os.RemoteException;
-import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.util.Slog;
 
@@ -47,6 +46,13 @@
     private static final String TAG = "BiometricManager";
 
     /**
+     * An ID that should match any biometric sensor on the device.
+     *
+     * @hide
+     */
+    public static final int SENSOR_ID_ANY = -1;
+
+    /**
      * No error detected.
      */
     public static final int BIOMETRIC_SUCCESS =
@@ -139,7 +145,7 @@
          *
          * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          */
         int BIOMETRIC_STRONG = 0x000F;
 
@@ -182,7 +188,7 @@
          * <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
          * generation.
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          */
         int DEVICE_CREDENTIAL = 1 << 15;
     }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 76cf9b9..4f6a7c7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,7 +36,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.identity.IdentityCredential;
-import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 import android.util.Log;
@@ -325,7 +324,7 @@
          * request authentication with the proper set of authenticators (e.g. match the
          * authenticators specified during key generation).
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          * @see KeyProperties#AUTH_BIOMETRIC_STRONG
          * @see KeyProperties#AUTH_DEVICE_CREDENTIAL
          *
@@ -365,6 +364,21 @@
         }
 
         /**
+         * If set, authenticate using the biometric sensor with the given ID.
+         *
+         * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default).
+         * @return This builder.
+         *
+         * @hide
+         */
+        @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+        @NonNull
+        public Builder setSensorId(int sensorId) {
+            mPromptInfo.setSensorId(sensorId);
+            return this;
+        }
+
+        /**
          * Creates a {@link BiometricPrompt}.
          *
          * @return An instance of {@link BiometricPrompt}.
@@ -589,7 +603,8 @@
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
-     * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+     * {@code setUserAuthenticationParameters(int, int)} method of {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder}.
      *
      * <p>CryptoObjects are used to unlock auth-per-use keys via
      * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
@@ -778,6 +793,27 @@
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback,
             int userId) {
+        authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */);
+    }
+
+    /**
+     * Authenticates for the given user and keystore operation.
+     *
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     * @param userId The user to authenticate
+     * @param operationId The keystore operation associated with authentication
+     *
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void authenticateUserForOperation(
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback,
+            int userId,
+            long operationId) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
         }
@@ -787,7 +823,7 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must supply a callback");
         }
-        authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
+        authenticateInternal(operationId, cancel, executor, callback, userId);
     }
 
     /**
@@ -912,11 +948,31 @@
         }
     }
 
-    private void authenticateInternal(@Nullable CryptoObject crypto,
+    private void authenticateInternal(
+            @Nullable CryptoObject crypto,
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback,
             int userId) {
+
+        mCryptoObject = crypto;
+        final long operationId = crypto != null ? crypto.getOpId() : 0L;
+        authenticateInternal(operationId, cancel, executor, callback, userId);
+    }
+
+    private void authenticateInternal(
+            long operationId,
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback,
+            int userId) {
+
+        // Ensure we don't return the wrong crypto object as an auth result.
+        if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) {
+            Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null");
+            mCryptoObject = null;
+        }
+
         try {
             if (cancel.isCanceled()) {
                 Log.w(TAG, "Authentication already canceled");
@@ -925,13 +981,11 @@
                 cancel.setOnCancelListener(new OnAuthenticationCancelListener());
             }
 
-            mCryptoObject = crypto;
             mExecutor = executor;
             mAuthenticationCallback = callback;
-            final long operationId = crypto != null ? crypto.getOpId() : 0;
 
             final PromptInfo promptInfo;
-            if (crypto != null) {
+            if (operationId != 0L) {
                 // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth.
                 // Note that we use a new PromptInfo here so as to not overwrite the application's
                 // preference, since it is possible that the same prompt configuration be used
@@ -952,10 +1006,9 @@
 
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception while authenticating", e);
-            mExecutor.execute(() -> {
-                callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        mContext.getString(R.string.biometric_error_hw_unavailable));
-            });
+            mExecutor.execute(() -> callback.onAuthenticationError(
+                    BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                    mContext.getString(R.string.biometric_error_hw_unavailable)));
         }
     }
 }
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index c2eff7d..0e99f31 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -40,6 +40,7 @@
     private @BiometricManager.Authenticators.Types int mAuthenticators;
     private boolean mDisallowBiometricsIfPolicyExists;
     private boolean mReceiveSystemEvents;
+    private int mSensorId = -1;
 
     public PromptInfo() {
 
@@ -59,6 +60,7 @@
         mAuthenticators = in.readInt();
         mDisallowBiometricsIfPolicyExists = in.readBoolean();
         mReceiveSystemEvents = in.readBoolean();
+        mSensorId = in.readInt();
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -93,6 +95,7 @@
         dest.writeInt(mAuthenticators);
         dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
         dest.writeBoolean(mReceiveSystemEvents);
+        dest.writeInt(mSensorId);
     }
 
     public boolean containsPrivateApiConfigurations() {
@@ -166,6 +169,10 @@
         mReceiveSystemEvents = receiveSystemEvents;
     }
 
+    public void setSensorId(int sensorId) {
+        mSensorId = sensorId;
+    }
+
     // Getters
 
     public CharSequence getTitle() {
@@ -226,4 +233,8 @@
     public boolean isReceiveSystemEvents() {
         return mReceiveSystemEvents;
     }
+
+    public int getSensorId() {
+        return mSensorId;
+    }
 }
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 29a6ee2..f175e7b 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,8 +16,12 @@
 
 package android.hardware.devicestate;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 
 import java.util.concurrent.Executor;
@@ -28,13 +32,19 @@
  *
  * @hide
  */
+@TestApi
 @SystemService(Context.DEVICE_STATE_SERVICE)
 public final class DeviceStateManager {
-    /** Invalid device state. */
+    /**
+     * Invalid device state.
+     *
+     * @hide
+     */
     public static final int INVALID_DEVICE_STATE = -1;
 
-    private DeviceStateManagerGlobal mGlobal;
+    private final DeviceStateManagerGlobal mGlobal;
 
+    /** @hide */
     public DeviceStateManager() {
         DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
         if (global == null) {
@@ -45,23 +55,73 @@
     }
 
     /**
+     * Returns the list of device states that are supported and can be requested with
+     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     */
+    @NonNull
+    public int[] getSupportedStates() {
+        return mGlobal.getSupportedStates();
+    }
+
+    /**
+     * Submits a {@link DeviceStateRequest request} to modify the device state.
+     * <p>
+     * By default, the request is kept active until a call to
+     * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
+     * <ul>
+     *     <li>Another processes submits a request succeeding this request in which case the request
+     *     will be suspended until the interrupting request is canceled.
+     *     <li>The requested state has become unsupported.
+     *     <li>The process submitting the request dies.
+     * </ul>
+     * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
+     *
+     * @throws IllegalArgumentException if the requested state is unsupported.
+     * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+     * permission is not held.
+     *
+     * @see DeviceStateRequest
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+    public void requestState(@NonNull DeviceStateRequest request,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable DeviceStateRequest.Callback callback) {
+        mGlobal.requestState(request, callback, executor);
+    }
+
+    /**
+     * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     * <p>
+     * This method is noop if the {@code request} has not been submitted with a call to
+     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     *
+     * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+     * permission is not held.
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+    public void cancelRequest(@NonNull DeviceStateRequest request) {
+        mGlobal.cancelRequest(request);
+    }
+
+    /**
      * Registers a listener to receive notifications about changes in device state.
      *
-     * @param listener the listener to register.
      * @param executor the executor to process notifications.
+     * @param listener the listener to register.
      *
      * @see DeviceStateListener
      */
-    public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
-            @NonNull Executor executor) {
+    public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull DeviceStateListener listener) {
         mGlobal.registerDeviceStateListener(listener, executor);
     }
 
     /**
      * Unregisters a listener previously registered with
-     * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+     * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
      */
-    public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
+    public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
         mGlobal.unregisterDeviceStateListener(listener);
     }
 
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c8905038..b9ae88e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -20,9 +20,11 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.ArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +69,9 @@
 
     @GuardedBy("mLock")
     private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+    @GuardedBy("mLock")
+    private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
+
     @Nullable
     @GuardedBy("mLock")
     private Integer mLastReceivedState;
@@ -77,9 +82,84 @@
     }
 
     /**
+     * Returns the set of supported device states.
+     *
+     * @see DeviceStateManager#getSupportedStates()
+     */
+    public int[] getSupportedStates() {
+        try {
+            return mDeviceStateManager.getSupportedDeviceStates();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Submits a {@link DeviceStateRequest request} to modify the device state.
+     *
+     * @see DeviceStateManager#requestState(DeviceStateRequest,
+     * Executor, DeviceStateRequest.Callback)
+     * @see DeviceStateRequest
+     */
+    public void requestState(@NonNull DeviceStateRequest request,
+            @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+        if (callback == null && executor != null) {
+            throw new IllegalArgumentException("Callback must be supplied with executor.");
+        } else if (executor == null && callback != null) {
+            throw new IllegalArgumentException("Executor must be supplied with callback.");
+        }
+
+        synchronized (mLock) {
+            registerCallbackIfNeededLocked();
+
+            if (findRequestTokenLocked(request) != null) {
+                // This request has already been submitted.
+                return;
+            }
+
+            // Add the request wrapper to the mRequests array before requesting the state as the
+            // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+            // same process as this instance.
+            IBinder token = new Binder();
+            mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+
+            try {
+                mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
+            } catch (RemoteException ex) {
+                mRequests.remove(token);
+                throw ex.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+     * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+     *
+     * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
+     */
+    public void cancelRequest(@NonNull DeviceStateRequest request) {
+        synchronized (mLock) {
+            registerCallbackIfNeededLocked();
+
+            final IBinder token = findRequestTokenLocked(request);
+            if (token == null) {
+                // This request has not been submitted.
+                return;
+            }
+
+            try {
+                mDeviceStateManager.cancelRequest(token);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Registers a listener to receive notifications about changes in device state.
      *
-     * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+     * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
      */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@@ -112,7 +192,7 @@
      * Unregisters a listener previously registered with
      * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
      *
-     * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+     * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
      */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public void unregisterDeviceStateListener(DeviceStateListener listener) {
@@ -144,6 +224,17 @@
         return -1;
     }
 
+    @Nullable
+    private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
+        for (int i = 0; i < mRequests.size(); i++) {
+            if (mRequests.valueAt(i).mRequest.equals(request)) {
+                return mRequests.keyAt(i);
+            }
+        }
+        return null;
+    }
+
+    /** Handles a call from the server that the device state has changed. */
     private void handleDeviceStateChanged(int newDeviceState) {
         ArrayList<DeviceStateListenerWrapper> listeners;
         synchronized (mLock) {
@@ -156,11 +247,68 @@
         }
     }
 
+    /**
+     * Handles a call from the server that a request for the supplied {@code token} has become
+     * active.
+     */
+    private void handleRequestActive(IBinder token) {
+        DeviceStateRequestWrapper request;
+        synchronized (mLock) {
+            request = mRequests.get(token);
+        }
+        if (request != null) {
+            request.notifyRequestActive();
+        }
+    }
+
+    /**
+     * Handles a call from the server that a request for the supplied {@code token} has become
+     * suspended.
+     */
+    private void handleRequestSuspended(IBinder token) {
+        DeviceStateRequestWrapper request;
+        synchronized (mLock) {
+            request = mRequests.get(token);
+        }
+        if (request != null) {
+            request.notifyRequestSuspended();
+        }
+    }
+
+    /**
+     * Handles a call from the server that a request for the supplied {@code token} has become
+     * canceled.
+     */
+    private void handleRequestCanceled(IBinder token) {
+        DeviceStateRequestWrapper request;
+        synchronized (mLock) {
+            request = mRequests.remove(token);
+        }
+        if (request != null) {
+            request.notifyRequestCanceled();
+        }
+    }
+
     private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
         @Override
         public void onDeviceStateChanged(int deviceState) {
             handleDeviceStateChanged(deviceState);
         }
+
+        @Override
+        public void onRequestActive(IBinder token) {
+            handleRequestActive(token);
+        }
+
+        @Override
+        public void onRequestSuspended(IBinder token) {
+            handleRequestSuspended(token);
+        }
+
+        @Override
+        public void onRequestCanceled(IBinder token) {
+            handleRequestCanceled(token);
+        }
     }
 
     private static final class DeviceStateListenerWrapper {
@@ -176,4 +324,43 @@
             mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
         }
     }
+
+    private static final class DeviceStateRequestWrapper {
+        private final DeviceStateRequest mRequest;
+        @Nullable
+        private final DeviceStateRequest.Callback mCallback;
+        @Nullable
+        private final Executor mExecutor;
+
+        DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
+                @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+            mRequest = request;
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        void notifyRequestActive() {
+            if (mCallback == null) {
+                return;
+            }
+
+            mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
+        }
+
+        void notifyRequestSuspended() {
+            if (mCallback == null) {
+                return;
+            }
+
+            mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+        }
+
+        void notifyRequestCanceled() {
+            if (mCallback == null) {
+                return;
+            }
+
+            mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+        }
+    }
 }
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
new file mode 100644
index 0000000..70f7002
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.hardware.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A request to alter the state of the device managed by {@link DeviceStateManager}.
+ * <p>
+ * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to
+ * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
+ * occurs:
+ * <ul>
+ *     <li>Another processes submits a request succeeding this request in which case the request
+ *     will be suspended until the interrupting request is canceled.
+ *     <li>The requested state has become unsupported.
+ *     <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the request. For example, the
+ * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the
+ * request whenever the base (non-override) device state changes.
+ *
+ * @see DeviceStateManager
+ *
+ * @hide
+ */
+@TestApi
+public final class DeviceStateRequest {
+    /**
+     * Flag that indicates the request should be canceled automatically when the base
+     * (non-override) device state changes. Useful when the requestor only wants the request to
+     * remain active while the base state remains constant and automatically cancel when the user
+     * manipulates the device into a different state.
+     */
+    public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0;
+
+    /** @hide */
+    @IntDef(prefix = {"FLAG_"}, flag = true, value = {
+            FLAG_CANCEL_WHEN_BASE_CHANGES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequestFlags {}
+
+    /**
+     * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported
+     * states for the device which can be queried with a call to
+     * {@link DeviceStateManager#getSupportedStates()}.
+     *
+     * @param requestedState the device state being requested.
+     */
+    @NonNull
+    public static Builder newBuilder(int requestedState) {
+        return new Builder(requestedState);
+    }
+
+    /**
+     * Builder for {@link DeviceStateRequest}. An instance can be obtained through
+     * {@link #newBuilder(int)}.
+     */
+    public static final class Builder {
+        private final int mRequestedState;
+        private int mFlags;
+
+        private Builder(int requestedState) {
+            mRequestedState = requestedState;
+        }
+
+        /**
+         * Sets the flag bits provided within {@code flags} with all other bits remaining
+         * unchanged.
+         */
+        @NonNull
+        public Builder setFlags(@RequestFlags int flags) {
+            mFlags |= flags;
+            return this;
+        }
+
+        /**
+         * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the
+         * builder.
+         */
+        @NonNull
+        public DeviceStateRequest build() {
+            return new DeviceStateRequest(mRequestedState, mFlags);
+        }
+    }
+
+    /** Callback to track the status of a request. */
+    public interface Callback {
+        /**
+         * Called to indicate the request has become active and the device state will match the
+         * requested state.
+         * <p>
+         * Guaranteed to be called after a call to
+         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+         * matching the requested state.
+         */
+        default void onRequestActivated(@NonNull DeviceStateRequest request) {}
+
+        /**
+         * Called to indicate the request has been temporarily suspended.
+         * <p>
+         * Guaranteed to be called before a call to
+         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+         */
+        default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
+
+        /**
+         * Called to indicate the request has been canceled. The request can be resubmitted with
+         * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+         * DeviceStateRequest.Callback)}.
+         * <p>
+         * Guaranteed to be called before a call to
+         * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+         * <p>
+         * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
+         * occur before this method.
+         */
+        default void onRequestCanceled(@NonNull DeviceStateRequest request) {}
+    }
+
+    private final int mRequestedState;
+    @RequestFlags
+    private final int mFlags;
+
+    private DeviceStateRequest(int requestedState, @RequestFlags int flags) {
+        mRequestedState = requestedState;
+        mFlags = flags;
+    }
+
+    public int getState() {
+        return mRequestedState;
+    }
+
+    @RequestFlags
+    public int getFlags() {
+        return mFlags;
+    }
+}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index a157b33..323ad21 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -20,5 +20,45 @@
 
 /** @hide */
 interface IDeviceStateManager {
+    /**
+     * Registers a callback to receive notifications from the device state manager. Only one
+     * callback can be registered per-process.
+     * <p>
+     * As the callback mechanism is used to alert the caller of changes to request status a callback
+     * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
+     * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
+     *
+     * @throws SecurityException if a callback is already registered for the calling process.
+     */
     void registerCallback(in IDeviceStateManagerCallback callback);
+
+    /** Returns the array of supported device state identifiers. */
+    int[] getSupportedDeviceStates();
+
+    /**
+     * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
+     * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
+     * call to this method.
+     *
+     * @param token the request token previously registered with
+     *        {@link #requestState(IBinder, int, int)}
+     *
+     * @throws IllegalStateException if a callback has not yet been registered for the calling
+     *         process.
+     * @throws IllegalStateException if the supplied {@code token} has already been registered.
+     * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+     */
+    void requestState(IBinder token, int state, int flags);
+
+    /**
+     * Cancels a request previously submitted with a call to
+     * {@link #requestState(IBinder, int, int)}.
+     *
+     * @param token the request token previously registered with
+     *        {@link #requestState(IBinder, int, int)}
+     *
+     * @throws IllegalStateException if the supplied {@code token} has not been previously
+     *         requested.
+     */
+    void cancelRequest(IBinder token);
 }
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index d1c5813..ee2a071 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -18,5 +18,42 @@
 
 /** @hide */
 interface IDeviceStateManagerCallback {
+    /**
+     * Called in response to a change in device state. Guaranteed to be called once with the initial
+     * value on registration of the callback.
+     *
+     * @param deviceState the new state of the device.
+     */
     oneway void onDeviceStateChanged(int deviceState);
+
+    /**
+     * Called to notify the callback that a request has become active. Guaranteed to be called
+     * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
+     * resulted in a device state change.
+     *
+     * @param token the request token previously registered with
+     *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
+     */
+    oneway void onRequestActive(IBinder token);
+
+    /**
+     * Called to notify the callback that a request has become suspended. Guaranteed to be called
+     * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
+     * suspended resulted in a device state change.
+     *
+     * @param token the request token previously registered with
+     *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
+     */
+    oneway void onRequestSuspended(IBinder token);
+
+    /**
+     * Called to notify the callback that a request has become canceled. No further callbacks will
+     * be triggered for this request. Guaranteed to be called before a subsequent call to
+     * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
+     * state change.
+     *
+     * @param token the request token previously registered with
+     *        {@link IDeviceStateManager#requestState(IBinder, int, int)}
+     */
+    oneway void onRequestCanceled(IBinder token);
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 9bae1ff..bbf421d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -854,8 +854,8 @@
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(float brightness) {
-        mGlobal.setTemporaryBrightness(brightness);
+    public void setTemporaryBrightness(int displayId, float brightness) {
+        mGlobal.setTemporaryBrightness(displayId, brightness);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 77ae947..60fe582 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -636,13 +636,13 @@
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
      * </p>
      *
-     * @param brightness The brightness value from 0 to 255.
+     * @param brightness The brightness value from 0.0f to 1.0f.
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(float brightness) {
+    public void setTemporaryBrightness(int displayId, float brightness) {
         try {
-            mDm.setTemporaryBrightness(brightness);
+            mDm.setTemporaryBrightness(displayId, brightness);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a9f78fa..ff8a720 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -116,7 +116,7 @@
     boolean isMinimalPostProcessingRequested(int displayId);
 
     // Temporarily sets the display brightness.
-    void setTemporaryBrightness(float brightness);
+    void setTemporaryBrightness(int displayId, float brightness);
 
     // Temporarily sets the auto brightness adjustment factor.
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
similarity index 68%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/hardware/face/FaceAuthenticationFrame.aidl
index e0eb06cb..4dc41f1 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,18 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.hardware.face;
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
-}
+/**
+ * @hide
+ */
+parcelable FaceAuthenticationFrame;
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
new file mode 100644
index 0000000..f39d634
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -0,0 +1,75 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face authentication.
+ *
+ * @hide
+ */
+public final class FaceAuthenticationFrame implements Parcelable {
+    @NonNull private final FaceDataFrame mData;
+
+    /**
+     * Data model for a frame captured during face authentication.
+     *
+     * @param data Information about the current frame.
+     */
+    public FaceAuthenticationFrame(@NonNull FaceDataFrame data) {
+        mData = data;
+    }
+
+    /**
+     * @return Information about the current frame.
+     */
+    @NonNull
+    public FaceDataFrame getData() {
+        return mData;
+    }
+
+    private FaceAuthenticationFrame(@NonNull Parcel source) {
+        mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mData, flags);
+    }
+
+    public static final Creator<FaceAuthenticationFrame> CREATOR =
+            new Creator<FaceAuthenticationFrame>() {
+
+        @Override
+        public FaceAuthenticationFrame createFromParcel(Parcel source) {
+            return new FaceAuthenticationFrame(source);
+        }
+
+        @Override
+        public FaceAuthenticationFrame[] newArray(int size) {
+            return new FaceAuthenticationFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
new file mode 100644
index 0000000..092359c
--- /dev/null
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -0,0 +1,167 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @hide
+ */
+public final class FaceDataFrame implements Parcelable {
+    private final int mAcquiredInfo;
+    private final int mVendorCode;
+    private final float mPan;
+    private final float mTilt;
+    private final float mDistance;
+    private final boolean mIsCancellable;
+
+    /**
+     * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+     *
+     * @param acquiredInfo An integer corresponding to a known acquired message.
+     * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+     *  {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+     * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a
+     *  good capture.
+     * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a
+     *  good capture.
+     * @param distance The distance of the detected face from the device. Values in the range
+     *  [-1, 1] indicate a good capture.
+     * @param isCancellable Whether the ongoing face operation should be canceled.
+     */
+    public FaceDataFrame(
+            int acquiredInfo,
+            int vendorCode,
+            float pan,
+            float tilt,
+            float distance,
+            boolean isCancellable) {
+        mAcquiredInfo = acquiredInfo;
+        mVendorCode = vendorCode;
+        mPan = pan;
+        mTilt = tilt;
+        mDistance = distance;
+        mIsCancellable = isCancellable;
+    }
+
+    /**
+     * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+     *
+     * @param acquiredInfo An integer corresponding to a known acquired message.
+     * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+     *  {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+     */
+    public FaceDataFrame(int acquiredInfo, int vendorCode) {
+        mAcquiredInfo = acquiredInfo;
+        mVendorCode = vendorCode;
+        mPan = 0f;
+        mTilt = 0f;
+        mDistance = 0f;
+        mIsCancellable = false;
+    }
+
+    /**
+     * @return An integer corresponding to a known acquired message.
+     *
+     * @see android.hardware.biometrics.BiometricFaceConstants
+     */
+    public int getAcquiredInfo() {
+        return mAcquiredInfo;
+    }
+
+    /**
+     * @return An integer representing a custom vendor-specific message. Ignored unless
+     * {@code acquiredInfo} is {@link
+     * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}.
+     *
+     * @see android.hardware.biometrics.BiometricFaceConstants
+     */
+    public int getVendorCode() {
+        return mVendorCode;
+    }
+
+    /**
+     * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good
+     * capture.
+     */
+    public float getPan() {
+        return mPan;
+    }
+
+    /**
+     * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good
+     * capture.
+     */
+    public float getTilt() {
+        return mTilt;
+    }
+
+    /**
+     * @return The distance of the detected face from the device. Values in the range [-1, 1]
+     * indicate a good capture.
+     */
+    public float getDistance() {
+        return mDistance;
+    }
+
+    /**
+     * @return Whether the ongoing face operation should be canceled.
+     */
+    public boolean isCancellable() {
+        return mIsCancellable;
+    }
+
+    private FaceDataFrame(@NonNull Parcel source) {
+        mAcquiredInfo = source.readInt();
+        mVendorCode = source.readInt();
+        mPan = source.readFloat();
+        mTilt = source.readFloat();
+        mDistance = source.readFloat();
+        mIsCancellable = source.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAcquiredInfo);
+        dest.writeInt(mVendorCode);
+        dest.writeFloat(mPan);
+        dest.writeFloat(mTilt);
+        dest.writeFloat(mDistance);
+        dest.writeBoolean(mIsCancellable);
+    }
+
+    public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() {
+        @Override
+        public FaceDataFrame createFromParcel(Parcel source) {
+            return new FaceDataFrame(source);
+        }
+
+        @Override
+        public FaceDataFrame[] newArray(int size) {
+            return new FaceDataFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java
new file mode 100644
index 0000000..8415419
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollCell.java
@@ -0,0 +1,96 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollCell implements Parcelable {
+    private final int mX;
+    private final int mY;
+    private final int mZ;
+
+    /**
+     * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+     *
+     * @param x The horizontal coordinate of this cell.
+     * @param y The vertical coordinate of this cell.
+     * @param z The depth coordinate of this cell.
+     */
+    public FaceEnrollCell(int x, int y, int z) {
+        mX = x;
+        mY = y;
+        mZ = z;
+    }
+
+    /**
+     * @return The horizontal coordinate of this cell.
+     */
+    public int getX() {
+        return mX;
+    }
+
+    /**
+     * @return The vertical coordinate of this cell.
+     */
+    public int getY() {
+        return mY;
+    }
+
+    /**
+     * @return The depth coordinate of this cell.
+     */
+    public int getZ() {
+        return mZ;
+    }
+
+    private FaceEnrollCell(@NonNull Parcel source) {
+        mX = source.readInt();
+        mY = source.readInt();
+        mZ = source.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mX);
+        dest.writeInt(mY);
+        dest.writeInt(mZ);
+    }
+
+    public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() {
+        @Override
+        public FaceEnrollCell createFromParcel(Parcel source) {
+            return new FaceEnrollCell(source);
+        }
+
+        @Override
+        public FaceEnrollCell[] newArray(int size) {
+            return new FaceEnrollCell[size];
+        }
+    };
+}
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/face/FaceEnrollFrame.aidl
similarity index 68%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/hardware/face/FaceEnrollFrame.aidl
index e0eb06cb..b854681 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/hardware/face/FaceEnrollFrame.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,18 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.hardware.face;
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
-}
+/**
+ * @hide
+ */
+parcelable FaceEnrollFrame;
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
new file mode 100644
index 0000000..551139d
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollFrame implements Parcelable {
+    @Nullable private final FaceEnrollCell mCell;
+    @FaceEnrollStage private final int mStage;
+    @NonNull private final FaceDataFrame mData;
+
+    /**
+     * Data model for a frame captured during face enrollment.
+     *
+     * @param cell The cell captured during this frame of enrollment, if any.
+     * @param stage An integer representing the current stage of enrollment.
+     * @param data Information about the current frame.
+     */
+    public FaceEnrollFrame(
+            @Nullable FaceEnrollCell cell,
+            @FaceEnrollStage int stage,
+            @NonNull FaceDataFrame data) {
+        mCell = cell;
+        mStage = stage;
+        mData = data;
+    }
+
+    /**
+     * @return The cell captured during this frame of enrollment, if any.
+     */
+    @Nullable
+    public FaceEnrollCell getCell() {
+        return mCell;
+    }
+
+    /**
+     * @return An integer representing the current stage of enrollment.
+     */
+    @FaceEnrollStage
+    public int getStage() {
+        return mStage;
+    }
+
+    /**
+     * @return Information about the current frame.
+     */
+    @NonNull
+    public FaceDataFrame getData() {
+        return mData;
+    }
+
+    private FaceEnrollFrame(@NonNull Parcel source) {
+        mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+        mStage = source.readInt();
+        mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mCell, flags);
+        dest.writeInt(mStage);
+        dest.writeParcelable(mData, flags);
+    }
+
+    public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() {
+        @Override
+        public FaceEnrollFrame createFromParcel(Parcel source) {
+            return new FaceEnrollFrame(source);
+        }
+
+        @Override
+        public FaceEnrollFrame[] newArray(int size) {
+            return new FaceEnrollFrame[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
new file mode 100644
index 0000000..de717fb
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -0,0 +1,74 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A stage that may occur during face enrollment.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+    FaceEnrollStage.UNKNOWN,
+    FaceEnrollStage.FIRST_FRAME_RECEIVED,
+    FaceEnrollStage.WAITING_FOR_CENTERING,
+    FaceEnrollStage.HOLD_STILL_IN_CENTER,
+    FaceEnrollStage.ENROLLING_MOVEMENT_1,
+    FaceEnrollStage.ENROLLING_MOVEMENT_2,
+    FaceEnrollStage.ENROLLMENT_FINISHED
+})
+public @interface FaceEnrollStage {
+    /**
+     * The current enrollment stage is not known.
+     */
+    int UNKNOWN = -1;
+
+    /**
+     * Enrollment has just begun. No action is needed from the user yet.
+     */
+    int FIRST_FRAME_RECEIVED = 0;
+
+    /**
+     * The user must center their face in the frame.
+     */
+    int WAITING_FOR_CENTERING = 1;
+
+    /**
+     * The user must keep their face centered in the frame.
+     */
+    int HOLD_STILL_IN_CENTER = 2;
+
+    /**
+     * The user must follow a first set of movement instructions.
+     */
+    int ENROLLING_MOVEMENT_1 = 3;
+
+    /**
+     * The user must follow a second set of movement instructions.
+     */
+    int ENROLLING_MOVEMENT_2 = 4;
+
+    /**
+     * Enrollment has completed. No more action is needed from the user.
+     */
+    int ENROLLMENT_FINISHED = 5;
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 588bc01..f3da6a9 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -71,17 +71,19 @@
     private static final int MSG_FACE_DETECTED = 109;
     private static final int MSG_CHALLENGE_INTERRUPTED = 110;
     private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
+    private static final int MSG_AUTHENTICATION_FRAME = 112;
+    private static final int MSG_ENROLLMENT_FRAME = 113;
 
     private final IFaceService mService;
     private final Context mContext;
     private IBinder mToken = new Binder();
-    private AuthenticationCallback mAuthenticationCallback;
-    private FaceDetectionCallback mFaceDetectionCallback;
-    private EnrollmentCallback mEnrollmentCallback;
-    private RemovalCallback mRemovalCallback;
-    private SetFeatureCallback mSetFeatureCallback;
-    private GetFeatureCallback mGetFeatureCallback;
-    private GenerateChallengeCallback mGenerateChallengeCallback;
+    @Nullable private AuthenticationCallback mAuthenticationCallback;
+    @Nullable private FaceDetectionCallback mFaceDetectionCallback;
+    @Nullable private EnrollmentCallback mEnrollmentCallback;
+    @Nullable private RemovalCallback mRemovalCallback;
+    @Nullable private SetFeatureCallback mSetFeatureCallback;
+    @Nullable private GetFeatureCallback mGetFeatureCallback;
+    @Nullable private GenerateChallengeCallback mGenerateChallengeCallback;
     private CryptoObject mCryptoObject;
     private Face mRemovalFace;
     private Handler mHandler;
@@ -154,6 +156,16 @@
         public void onChallengeInterruptFinished(int sensorId) {
             mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
         }
+
+        @Override
+        public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
+        }
+
+        @Override
+        public void onEnrollmentFrame(FaceEnrollFrame frame) {
+            mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget();
+        }
     };
 
     /**
@@ -1248,6 +1260,12 @@
                 case MSG_CHALLENGE_INTERRUPT_FINISHED:
                     sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
                     break;
+                case MSG_AUTHENTICATION_FRAME:
+                    sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
+                    break;
+                case MSG_ENROLLMENT_FRAME:
+                    sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */);
+                    break;
                 default:
                     Slog.w(TAG, "Unknown message: " + msg.what);
             }
@@ -1349,15 +1367,52 @@
 
     private void sendAcquiredResult(int acquireInfo, int vendorCode) {
         if (mAuthenticationCallback != null) {
+            final FaceAuthenticationFrame frame = new FaceAuthenticationFrame(
+                    new FaceDataFrame(acquireInfo, vendorCode));
+            sendAuthenticationFrame(frame);
+        } else if (mEnrollmentCallback != null) {
+            final FaceEnrollFrame frame = new FaceEnrollFrame(
+                    null /* cell */,
+                    FaceEnrollStage.UNKNOWN,
+                    new FaceDataFrame(acquireInfo, vendorCode));
+            sendEnrollmentFrame(frame);
+        }
+    }
+
+    private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) {
+        if (frame == null) {
+            Slog.w(TAG, "Received null authentication frame");
+        } else if (mAuthenticationCallback != null) {
+            // TODO(b/178414967): Send additional frame data to callback
+            final int acquireInfo = frame.getData().getAcquiredInfo();
+            final int vendorCode = frame.getData().getVendorCode();
+            final int helpCode = getHelpCode(acquireInfo, vendorCode);
+            final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode);
             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+
+            // Ensure that only non-null help messages are sent.
+            if (helpMessage != null) {
+                mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage);
+            }
         }
-        final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
-        final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
-                ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
-        if (mEnrollmentCallback != null) {
-            mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
-        } else if (mAuthenticationCallback != null && msg != null) {
-            mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+    }
+
+    private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) {
+        if (frame == null) {
+            Slog.w(TAG, "Received null enrollment frame");
+        } else if (mEnrollmentCallback != null) {
+            // TODO(b/178414967): Send additional frame data to callback
+            final int acquireInfo = frame.getData().getAcquiredInfo();
+            final int vendorCode = frame.getData().getVendorCode();
+            final int helpCode = getHelpCode(acquireInfo, vendorCode);
+            final String helpMessage = getAcquiredString(mContext, acquireInfo, vendorCode);
+            mEnrollmentCallback.onEnrollmentHelp(helpCode, helpMessage);
         }
     }
+
+    private static int getHelpCode(int acquireInfo, int vendorCode) {
+        return acquireInfo == FACE_ACQUIRED_VENDOR
+                ? vendorCode + FACE_ACQUIRED_VENDOR_BASE
+                : acquireInfo;
+    }
 }
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index bd4d3a0..2ef1430 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,6 +16,8 @@
 package android.hardware.face;
 
 import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
 
 /**
  * Communication channel from the FaceService back to FaceAuthenticationManager.
@@ -34,4 +36,6 @@
     void onChallengeGenerated(int sensorId, long challenge);
     void onChallengeInterrupted(int sensorId);
     void onChallengeInterruptFinished(int sensorId);
+    void onAuthenticationFrame(in FaceAuthenticationFrame frame);
+    void onEnrollmentFrame(in FaceEnrollFrame frame);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d932865..188a2a4 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -93,17 +93,26 @@
     private static final int MSG_UDFPS_POINTER_UP = 109;
 
     /**
-     * Request authentication with any single sensor.
      * @hide
      */
-    public static final int SENSOR_ID_ANY = -1;
+    public static final int ENROLL_FIND_SENSOR = 1;
+    /**
+     * @hide
+     */
+    public static final int ENROLL_ENROLL = 2;
 
     /**
      * @hide
      */
-    @IntDef({SENSOR_ID_ANY})
+    @IntDef({ENROLL_FIND_SENSOR, ENROLL_ENROLL})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface SensorId {}
+    public @interface EnrollReason {}
+
+    /**
+     * Request authentication with any single sensor.
+     * @hide
+     */
+    public static final int SENSOR_ID_ANY = -1;
 
     private IFingerprintService mService;
     private Context mContext;
@@ -508,8 +517,8 @@
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId,
-            int userId) {
+            @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -590,7 +599,7 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
-            EnrollmentCallback callback, boolean shouldLogMetrics) {
+            EnrollmentCallback callback, @EnrollReason int enrollReason) {
         if (userId == UserHandle.USER_CURRENT) {
             userId = getCurrentUserId();
         }
@@ -611,7 +620,7 @@
             try {
                 mEnrollmentCallback = callback;
                 mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
-                        mContext.getOpPackageName(), shouldLogMetrics);
+                        mContext.getOpPackageName(), enrollReason);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
@@ -653,15 +662,12 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void generateChallenge(int userId, GenerateChallengeCallback callback) {
-        final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
-                getSensorPropertiesInternal();
-        if (fingerprintSensorProperties.isEmpty()) {
+        final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+        if (sensorProps == null) {
             Slog.e(TAG, "No sensors");
             return;
         }
-
-        final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-        generateChallenge(sensorId, userId, callback);
+        generateChallenge(sensorProps.sensorId, userId, callback);
     }
 
     /**
@@ -681,18 +687,18 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void revokeChallenge(int userId, long challenge) {
-        if (mService != null) try {
-            final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
-                    getSensorPropertiesInternal();
-            if (fingerprintSensorProperties.isEmpty()) {
-                Slog.e(TAG, "No sensors");
-                return;
+        if (mService != null) {
+            try {
+                final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+                if (sensorProps == null) {
+                    Slog.e(TAG, "No sensors");
+                    return;
+                }
+                mService.revokeChallenge(mToken, sensorProps.sensorId, userId,
+                        mContext.getOpPackageName(), challenge);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
             }
-            final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-            mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
-                    challenge);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1161,6 +1167,12 @@
         }
     }
 
+    @Nullable
+    private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() {
+        final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal();
+        return allSensors.isEmpty() ? null : allSensors.get(0);
+    }
+
     private void cancelEnrollment() {
         if (mService != null) try {
             mService.cancelEnrollment(mToken);
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index f097651..663a704 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -109,9 +109,10 @@
             this.sensorLocationY = props[1];
             this.sensorRadius = props[2];
         } else {
-            this.sensorLocationX = 0;
-            this.sensorLocationY = 0;
-            this.sensorRadius = 0;
+            // Fake coordinates that could be used for the fake UDFPS mode.
+            this.sensorLocationX = 540;
+            this.sensorLocationY = 1636;
+            this.sensorRadius = 130;
         }
     }
 
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3657a83..8888247 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -78,7 +78,7 @@
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
-            String opPackageName, boolean shouldLogMetrics);
+            String opPackageName, int enrollReason);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index c093489..81c7d89 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -21,10 +21,11 @@
  */
 oneway interface IUdfpsOverlayController {
     const int REASON_UNKNOWN = 0;
-    const int REASON_ENROLL = 1;
-    const int REASON_AUTH_BP = 2; // BiometricPrompt
-    const int REASON_AUTH_FPM_KEYGUARD = 3; // FingerprintManager usage from Keyguard
-    const int REASON_AUTH_FPM_OTHER = 4; // Other FingerprintManager usage
+    const int REASON_ENROLL_FIND_SENSOR = 1;
+    const int REASON_ENROLL_ENROLLING = 2;
+    const int REASON_AUTH_BP = 3; // BiometricPrompt
+    const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard
+    const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage
 
     // Shows the overlay.
     void showUdfpsOverlay(int sensorId, int reason);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c69c47f..eaa38f3 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -26,6 +26,7 @@
 import android.hardware.input.IInputSensorEventListener;
 import android.hardware.input.InputSensorInfo;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.VibrationEffect;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -92,6 +93,8 @@
     void cancelVibrate(int deviceId, IBinder token);
     int[] getVibratorIds(int deviceId);
     boolean isVibrating(int deviceId);
+    boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
+    boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
 
     // Input device battery query.
     int getBatteryStatus(int deviceId);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index c60d6ce..f4d8a65 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -18,10 +18,18 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.Context;
 import android.os.Binder;
+import android.os.IVibratorStateListener;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 
 import java.util.concurrent.Executor;
 
@@ -29,12 +37,18 @@
  * Vibrator implementation that communicates with the input device vibrators.
  */
 final class InputDeviceVibrator extends Vibrator {
+    private static final String TAG = "InputDeviceVibrator";
+
     // mDeviceId represents InputDevice ID the vibrator belongs to
     private final int mDeviceId;
     private final int mVibratorId;
     private final Binder mToken;
     private final InputManager mInputManager;
 
+    @GuardedBy("mDelegates")
+    private final ArrayMap<OnVibratorStateChangedListener,
+            OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+
     InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
         mInputManager = inputManager;
         mDeviceId = deviceId;
@@ -42,6 +56,23 @@
         mToken = new Binder();
     }
 
+    private class OnVibratorStateChangedListenerDelegate extends
+            IVibratorStateListener.Stub {
+        private final Executor mExecutor;
+        private final OnVibratorStateChangedListener mListener;
+
+        OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
+                @NonNull Executor executor) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onVibrating(boolean isVibrating) {
+            mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+        }
+    }
+
     @Override
     public boolean hasVibrator() {
         return true;
@@ -52,25 +83,73 @@
         return mInputManager.isVibrating(mDeviceId);
     }
 
-    /* TODO: b/161634264 Support Vibrator listener API in input devices */
+    /**
+     * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+     * If the listener was previously added and not removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     */
     @Override
     public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "addVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+        Context context = ActivityThread.currentApplication();
+        addVibratorStateListener(context.getMainExecutor(), listener);
     }
 
+    /**
+     * Adds a listener for vibrator state change. If the listener was previously added and not
+     * removed, this call will be ignored.
+     *
+     * @param listener Listener to be added.
+     * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
+     */
     @Override
     public void addVibratorStateListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "addVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+        Preconditions.checkNotNull(executor);
+
+        synchronized (mDelegates) {
+            // If listener is already registered, reject and return.
+            if (mDelegates.containsKey(listener)) {
+                Log.w(TAG, "Listener already registered.");
+                return;
+            }
+
+            final OnVibratorStateChangedListenerDelegate delegate =
+                    new OnVibratorStateChangedListenerDelegate(listener, executor);
+            if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) {
+                Log.w(TAG, "Failed to register vibrate state listener");
+                return;
+            }
+            mDelegates.put(listener, delegate);
+
+        }
     }
 
+    /**
+     * Removes the listener for vibrator state changes. If the listener was not previously
+     * registered, this call will do nothing.
+     *
+     * @param listener Listener to be removed.
+     */
     @Override
     public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "removeVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+
+        synchronized (mDelegates) {
+            // Check if the listener is registered, otherwise will return.
+            if (mDelegates.containsKey(listener)) {
+                final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
+
+                if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) {
+                    Log.w(TAG, "Failed to unregister vibrate state listener");
+                    return;
+                }
+                mDelegates.remove(listener);
+            }
+        }
     }
 
     @Override
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 185c59d..8a01c66 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@
 import android.os.CombinedVibrationEffect;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.InputEventInjectionSync;
 import android.os.Looper;
 import android.os.Message;
@@ -1484,6 +1485,32 @@
     }
 
     /**
+     * Register input device vibrator state listener
+     *
+     * @hide
+     */
+    public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        try {
+            return mIm.registerVibratorStateListener(deviceId, listener);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister input device vibrator state listener
+     *
+     * @hide
+     */
+    public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        try {
+            return mIm.unregisterVibratorStateListener(deviceId, listener);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets a sensor manager service associated with an input device, always create a new instance.
      * @return The sensor manager, never null.
      * @hide
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 44a2e97..7e2be01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1732,7 +1732,7 @@
                 // If app window has portrait orientation, regardless of what display orientation
                 // is, IME shouldn't use fullscreen-mode.
                 || (mInputEditorInfo.internalImeOptions
-                        & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+                        & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
             return false;
         }
         return true;
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 5d8122b..32b19a4 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -32,7 +32,7 @@
 
 /**
  * Network definition that includes strong identity. Analogous to combining
- * {@link NetworkInfo} and an IMSI.
+ * {@link NetworkCapabilities} and an IMSI.
  *
  * @hide
  */
@@ -160,7 +160,7 @@
      */
     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
             boolean defaultNetwork, @NetworkType int subType) {
-        final int type = state.networkInfo.getType();
+        final int legacyType = state.legacyNetworkType;
 
         String subscriberId = null;
         String networkId = null;
@@ -171,7 +171,7 @@
 
         subscriberId = state.subscriberId;
 
-        if (type == TYPE_WIFI) {
+        if (legacyType == TYPE_WIFI) {
             if (state.networkCapabilities.getSsid() != null) {
                 networkId = state.networkCapabilities.getSsid();
                 if (networkId == null) {
@@ -184,7 +184,7 @@
             }
         }
 
-        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+        return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
                 defaultNetwork);
     }
 
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index e1ef8b5..e466d2e 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -41,6 +42,7 @@
     public final Network network;
     public final String subscriberId;
     public final String networkId;
+    public final int legacyNetworkType;
 
     private NetworkState() {
         networkInfo = null;
@@ -49,17 +51,35 @@
         network = null;
         subscriberId = null;
         networkId = null;
+        legacyNetworkType = 0;
     }
 
+    public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            @Nullable String subscriberId, @Nullable String networkId) {
+        this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    // Constructor that used internally in ConnectivityService mainline module.
     public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
             @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
             String subscriberId, String networkId) {
+        this(networkInfo.getType(), networkInfo, linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo,
+            @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            String subscriberId, String networkId) {
         this.networkInfo = networkInfo;
         this.linkProperties = linkProperties;
         this.networkCapabilities = networkCapabilities;
         this.network = network;
         this.subscriberId = subscriberId;
         this.networkId = networkId;
+        this.legacyNetworkType = legacyNetworkType;
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
@@ -80,6 +100,7 @@
         network = in.readParcelable(null);
         subscriberId = in.readString();
         networkId = in.readString();
+        legacyNetworkType = in.readInt();
     }
 
     @Override
@@ -95,6 +116,7 @@
         out.writeParcelable(network, flags);
         out.writeString(subscriberId);
         out.writeString(networkId);
+        out.writeInt(legacyNetworkType);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9..5e56164 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -18,14 +18,14 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcelable;
-import android.util.SparseArray;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 
 /** @hide */
@@ -60,16 +60,16 @@
     public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
 
     @NonNull
-    private final SparseArray<List<String>> mNetworkMappings;
+    private final Bundle mNetworkMappings;
 
     @NonNull
-    public SparseArray<List<String>> getNetworkPreferences() {
-        return mNetworkMappings.clone();
+    public Map<String, Integer> getNetworkPreferences() {
+        return convertToUnmodifiableMap(mNetworkMappings);
     }
 
-    private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+    private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
         Objects.requireNonNull(networkMappings);
-        mNetworkMappings = networkMappings.clone();
+        mNetworkMappings = (Bundle) networkMappings.clone();
     }
 
     @Override
@@ -99,26 +99,45 @@
      * @hide
      */
     public static final class Builder {
-        private final SparseArray<List<String>> mNetworkMappings;
+        private final Bundle mNetworkMappings;
 
         public Builder() {
-            mNetworkMappings = new SparseArray<>();
+            mNetworkMappings = new Bundle();
+        }
+
+        public Builder(@NonNull final OemNetworkPreferences preferences) {
+            Objects.requireNonNull(preferences);
+            mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
         }
 
         /**
-         * Add a network preference for a list of packages.
+         * Add a network preference for a given package. Previously stored values for the given
+         * package will be overwritten.
          *
-         * @param preference the desired network preference to use
-         * @param packages   full package names (e.g.: "com.google.apps.contacts") for apps to use
-         *                   the given preference
+         * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+         *                    to use the given preference
+         * @param preference  the desired network preference to use
          * @return The builder to facilitate chaining.
          */
         @NonNull
-        public Builder addNetworkPreference(@OemNetworkPreference final int preference,
-                @NonNull List<String> packages) {
-            Objects.requireNonNull(packages);
-            mNetworkMappings.put(preference,
-                    Collections.unmodifiableList(new ArrayList<>(packages)));
+        public Builder addNetworkPreference(@NonNull final String packageName,
+                @OemNetworkPreference final int preference) {
+            Objects.requireNonNull(packageName);
+            mNetworkMappings.putInt(packageName, preference);
+            return this;
+        }
+
+        /**
+         * Remove a network preference for a given package.
+         *
+         * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+         *                    remove a preference for.
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public Builder removeNetworkPreference(@NonNull final String packageName) {
+            Objects.requireNonNull(packageName);
+            mNetworkMappings.remove(packageName);
             return this;
         }
 
@@ -131,6 +150,14 @@
         }
     }
 
+    private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+        final Map<String, Integer> networkPreferences = new HashMap<>();
+        for (final String key : bundle.keySet()) {
+            networkPreferences.put(key, bundle.getInt(key));
+        }
+        return Collections.unmodifiableMap(networkPreferences);
+    }
+
     /** @hide */
     @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
             OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@
 
     @Override
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        dest.writeSparseArray(mNetworkMappings);
+        dest.writeBundle(mNetworkMappings);
     }
 
     @Override
@@ -187,7 +214,7 @@
                 @Override
                 public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
                     return new OemNetworkPreferences(
-                            in.readSparseArray(getClass().getClassLoader()));
+                            in.readBundle(getClass().getClassLoader()));
                 }
             };
 }
diff --git a/core/java/android/net/VpnTransportInfo.java b/core/java/android/net/VpnTransportInfo.java
new file mode 100644
index 0000000..082fa58
--- /dev/null
+++ b/core/java/android/net/VpnTransportInfo.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import com.android.internal.util.MessageUtils;
+
+import java.util.Objects;
+
+/** @hide */
+public final class VpnTransportInfo implements TransportInfo, Parcelable {
+    private static final SparseArray<String> sTypeToString =
+            MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
+
+    /** Type of this VPN. */
+    @VpnManager.VpnType public final int type;
+
+    public VpnTransportInfo(@VpnManager.VpnType int type) {
+        this.type = type;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof VpnTransportInfo)) return false;
+
+        VpnTransportInfo that = (VpnTransportInfo) o;
+        return this.type == that.type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type);
+    }
+
+    @Override
+    public String toString() {
+        final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
+        return String.format("VpnTransportInfo{%s}", typeString);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(type);
+    }
+
+    public static final @NonNull Creator<VpnTransportInfo> CREATOR =
+            new Creator<VpnTransportInfo>() {
+        public VpnTransportInfo createFromParcel(Parcel in) {
+            return new VpnTransportInfo(in.readInt());
+        }
+        public VpnTransportInfo[] newArray(int size) {
+            return new VpnTransportInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index fa090f5..1a38338 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -28,8 +28,10 @@
 import android.os.ServiceSpecificException;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
@@ -67,8 +69,7 @@
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
-    @VisibleForTesting
-    public static final Map<
+    private static final Map<
                     VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
 
@@ -88,6 +89,18 @@
         mService = requireNonNull(service, "missing service");
     }
 
+    /**
+     * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    @NonNull
+    public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+            getAllPolicyListeners() {
+        return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
+    }
+
     // TODO: Make setVcnConfig(), clearVcnConfig() Public API
     /**
      * Sets the VCN configuration for a given subscription group.
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index af8e8de..305815f 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -33,20 +33,33 @@
     private final int mDischargePercentage;
     private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
     private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers;
+    private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers;
 
     private BatteryUsageStats(@NonNull Builder builder) {
         mConsumedPower = builder.mConsumedPower;
         mDischargePercentage = builder.mDischargePercentage;
+
         int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
         mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
         for (int i = 0; i < uidBatteryConsumerCount; i++) {
-            mUidBatteryConsumers.add(builder.mUidBatteryConsumerBuilders.valueAt(i).build());
+            UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
+                    builder.mUidBatteryConsumerBuilders.valueAt(i);
+            if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
+                mUidBatteryConsumers.add(uidBatteryConsumerBuilder.build());
+            }
         }
+
         int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size();
         mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount);
         for (int i = 0; i < systemBatteryConsumerCount; i++) {
             mSystemBatteryConsumers.add(builder.mSystemBatteryConsumerBuilders.valueAt(i).build());
         }
+
+        int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
+        mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
+        for (int i = 0; i < userBatteryConsumerCount; i++) {
+            mUserBatteryConsumers.add(builder.mUserBatteryConsumerBuilders.valueAt(i).build());
+        }
     }
 
     /**
@@ -75,6 +88,11 @@
         return mSystemBatteryConsumers;
     }
 
+    @NonNull
+    public List<UserBatteryConsumer> getUserBatteryConsumers() {
+        return mUserBatteryConsumers;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -85,6 +103,8 @@
         source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
         mSystemBatteryConsumers = new ArrayList<>();
         source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader());
+        mUserBatteryConsumers = new ArrayList<>();
+        source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader());
         mConsumedPower = source.readDouble();
         mDischargePercentage = source.readInt();
     }
@@ -93,6 +113,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelableList(mUidBatteryConsumers, flags);
         dest.writeParcelableList(mSystemBatteryConsumers, flags);
+        dest.writeParcelableList(mUserBatteryConsumers, flags);
         dest.writeDouble(mConsumedPower);
         dest.writeInt(mDischargePercentage);
     }
@@ -120,6 +141,8 @@
                 new SparseArray<>();
         private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
                 new SparseArray<>();
+        private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
+                new SparseArray<>();
 
         public Builder(int customPowerComponentCount, int customTimeComponentCount) {
             mCustomPowerComponentCount = customPowerComponentCount;
@@ -137,7 +160,6 @@
         /**
          * Sets the battery discharge amount since BatteryStats reset as percentage of the full
          * charge.
-         *
          */
         @SuppressLint("PercentageInt") // See b/174188159
         @NonNull
@@ -173,8 +195,8 @@
         }
 
         /**
-         * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution
-         * data for an individual UID.
+         * Creates or returns a exiting SystemBatteryConsumer, which represents battery attribution
+         * data for a specific drain type.
          */
         @NonNull
         public SystemBatteryConsumer.Builder getOrCreateSystemBatteryConsumerBuilder(
@@ -188,6 +210,21 @@
             return builder;
         }
 
+        /**
+         * Creates or returns a exiting UserBatteryConsumer, which represents battery attribution
+         * data for an individual {@link UserHandle}.
+         */
+        @NonNull
+        public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
+            UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
+            if (builder == null) {
+                builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount,
+                        mCustomTimeComponentCount, userId);
+                mUserBatteryConsumerBuilders.put(userId, builder);
+            }
+            return builder;
+        }
+
         @NonNull
         public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
             return mUidBatteryConsumerBuilders;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 48e7389..5b5fe1d 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.util.IntArray;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -53,9 +54,13 @@
     public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
 
     private final int mFlags;
+    @NonNull
+    private final int[] mUserIds;
 
     private BatteryUsageStatsQuery(@NonNull Builder builder) {
         mFlags = builder.mFlags;
+        mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
+                : new int[]{UserHandle.USER_ALL};
     }
 
     @BatteryUsageStatsFlags
@@ -63,13 +68,28 @@
         return mFlags;
     }
 
+    /**
+     * Returns an array of users for which the attribution is requested.  It may
+     * contain {@link UserHandle#USER_ALL} to indicate that the attribution
+     * should be performed for all users. Battery consumed by users <b>not</b> included
+     * in this array will be returned in the aggregated form as {@link UserBatteryConsumer}'s.
+     */
+    @NonNull
+    public int[] getUserIds() {
+        return mUserIds;
+    }
+
     private BatteryUsageStatsQuery(Parcel in) {
         mFlags = in.readInt();
+        mUserIds = new int[in.readInt()];
+        in.readIntArray(mUserIds);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mFlags);
+        dest.writeInt(mUserIds.length);
+        dest.writeIntArray(mUserIds);
     }
 
     @Override
@@ -96,6 +116,7 @@
      */
     public static final class Builder {
         private int mFlags;
+        private IntArray mUserIds;
 
         /**
          * Builds a read-only BatteryUsageStatsQuery object.
@@ -105,6 +126,18 @@
         }
 
         /**
+         * Add a user whose battery stats should be included in the battery usage stats.
+         * {@link UserHandle#USER_ALL} will be used by default if no users are added explicitly.
+         */
+        public Builder addUser(@NonNull UserHandle userHandle) {
+            if (mUserIds == null) {
+                mUserIds = new IntArray(1);
+            }
+            mUserIds.add(userHandle.getIdentifier());
+            return this;
+        }
+
+        /**
          * Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}.
          */
         public Builder setFlags(@BatteryUsageStatsFlags int flags) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9584bc7..6a76da2 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2557,6 +2557,14 @@
     public static native long getZramFreeKb();
 
     /**
+     * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
+     * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
+     *
+     * @hide
+     */
+    public static native long getDmabufTotalExportedKb();
+
+    /**
      * Return memory size in kilobytes allocated for ION heaps or -1 if
      * /sys/kernel/ion/total_heaps_kb could not be read.
      *
@@ -2565,6 +2573,14 @@
     public static native long getIonHeapsSizeKb();
 
     /**
+     * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
+     * /sys/kernel/dma_heap/total_pools_kb could not be read.
+     *
+     * @hide
+     */
+    public static native long getDmabufHeapPoolsSizeKb();
+
+    /**
      * Return memory size in kilobytes allocated for ION pools or -1 if
      * /sys/kernel/ion/total_pools_kb could not be read.
      *
@@ -2573,13 +2589,13 @@
     public static native long getIonPoolsSizeKb();
 
     /**
-     * Return ION memory mapped by processes in kB.
+     * Return DMA-BUF memory mapped by processes in kB.
      * Notes:
      *  * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process.
      *
      * @hide
      */
-    public static native long getIonMappedSizeKb();
+    public static native long getDmabufMappedSizeKb();
 
     /**
      * Return memory size in kilobytes used by GPU.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 518e29d..6295124 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.AppGlobals;
@@ -836,6 +837,21 @@
     public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
 
     /**
+     * Standard directory in which to place any audio files which are
+     * recordings.
+     */
+    @NonNull
+    // The better way is that expose a static method getRecordingDirectories.
+    // But since it's an existing API surface and developers already
+    // used to DIRECTORY_* constants, we should keep using this pattern
+    // for consistency. We use SuppressLint here to avoid exposing a final
+    // field. A final field will prevent us from ever changing the value of
+    // DIRECTORY_RECORDINGS. Not that it's likely that we will ever need to
+    // change it, but it's better to have such option.
+    @SuppressLint({"MutableBareField", "AllUpper"})
+    public static String DIRECTORY_RECORDINGS = "Recordings";
+
+    /**
      * List of standard storage directories.
      * <p>
      * Each of its values have its own constant:
@@ -851,6 +867,7 @@
      *   <li>{@link #DIRECTORY_DCIM}
      *   <li>{@link #DIRECTORY_DOCUMENTS}
      *   <li>{@link #DIRECTORY_AUDIOBOOKS}
+     *   <li>{@link #DIRECTORY_RECORDINGS}
      * </ul>
      * @hide
      */
@@ -866,6 +883,7 @@
             DIRECTORY_DCIM,
             DIRECTORY_DOCUMENTS,
             DIRECTORY_AUDIOBOOKS,
+            DIRECTORY_RECORDINGS,
     };
 
     /**
@@ -891,6 +909,7 @@
     /** {@hide} */ public static final int HAS_DCIM = 1 << 8;
     /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
     /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
+    /** {@hide} */ public static final int HAS_RECORDINGS = 1 << 11;
 
     /** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
     /** {@hide} */ public static final int HAS_OTHER = 1 << 17;
@@ -921,6 +940,7 @@
                 else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
                 else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
                 else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
+                else if (DIRECTORY_RECORDINGS.equals(name)) res |= HAS_RECORDINGS;
                 else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
                 else res |= HAS_OTHER;
             }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 39e3e14..8068c87 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1442,7 +1442,7 @@
      *
      * @hide
      */
-    public static boolean hasFileLocks(int pid) throws IOException {
+    public static boolean hasFileLocks(int pid) throws Exception {
         BufferedReader br = null;
 
         try {
@@ -1454,8 +1454,13 @@
 
                 for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
                     String str = st.nextToken();
-                    if (i == 4 && Integer.parseInt(str) == pid) {
-                        return true;
+                    try {
+                        if (i == 4 && Integer.parseInt(str) == pid) {
+                            return true;
+                        }
+                    } catch (NumberFormatException nfe) {
+                        throw new Exception("Exception parsing /proc/locks at \" "
+                                + line +  " \", token #" + i);
                     }
                 }
             }
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3ffaa9e..d1ca6a5 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -96,6 +96,7 @@
         private final int mUid;
         private String mPackageWithHighestDrain;
         private boolean mSystemComponent;
+        private boolean mExcludeFromBatteryUsageStats;
 
         public Builder(int customPowerComponentCount, int customTimeComponentCount,
                 BatteryStats.Uid batteryStatsUid) {
@@ -113,14 +114,6 @@
         }
 
         /**
-         * Creates a read-only object out of the Builder values.
-         */
-        @NonNull
-        public UidBatteryConsumer build() {
-            return new UidBatteryConsumer(this);
-        }
-
-        /**
          * Sets the name of the package owned by this UID that consumed the highest amount
          * of power since BatteryStats reset.
          */
@@ -131,12 +124,27 @@
         }
 
         /**
-         * Marks the UidBatteryConsumer as part of the system. For example,
-         * the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID} is considered
-         * as a system component.
+         * Marks the UidBatteryConsumer for exclusion from the result set.
          */
-        public void setSystemComponent(boolean systemComponent) {
-            mSystemComponent = systemComponent;
+        public Builder excludeFromBatteryUsageStats() {
+            mExcludeFromBatteryUsageStats = true;
+            return this;
+        }
+
+        /**
+         * Returns true if this UidBatteryConsumer must be excluded from the
+         * BatteryUsageStats.
+         */
+        public boolean isExcludedFromBatteryUsageStats() {
+            return mExcludeFromBatteryUsageStats;
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public UidBatteryConsumer build() {
+            return new UidBatteryConsumer(this);
         }
     }
 }
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
new file mode 100644
index 0000000..94e567f
--- /dev/null
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -0,0 +1,115 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains power consumption data attributed to a {@link UserHandle}.
+ *
+ * {@hide}
+ */
+public class UserBatteryConsumer extends BatteryConsumer implements Parcelable {
+    private final int mUserId;
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    private UserBatteryConsumer(@NonNull UserBatteryConsumer.Builder builder) {
+        super(builder.mPowerComponentsBuilder.build());
+        mUserId = builder.mUserId;
+    }
+
+    private UserBatteryConsumer(Parcel in) {
+        super(new PowerComponents(in));
+        mUserId = in.readInt();
+    }
+
+    /**
+     * Writes the contents into a Parcel.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mUserId);
+    }
+
+    public static final Creator<UserBatteryConsumer> CREATOR =
+            new Creator<UserBatteryConsumer>() {
+                @Override
+                public UserBatteryConsumer createFromParcel(Parcel in) {
+                    return new UserBatteryConsumer(in);
+                }
+
+                @Override
+                public UserBatteryConsumer[] newArray(int size) {
+                    return new UserBatteryConsumer[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Builder for UserBatteryConsumer.
+     */
+    public static final class Builder extends BaseBuilder<Builder> {
+        private final int mUserId;
+        private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
+
+        Builder(int customPowerComponentCount, int customTimeComponentCount, int userId) {
+            super(customPowerComponentCount, customTimeComponentCount);
+            mUserId = userId;
+        }
+
+        /**
+         * Add a UidBatteryConsumer to this UserBatteryConsumer.
+         * <p>
+         * Calculated power and duration components of the added UID battery consumers
+         * are aggregated at the time the UserBatteryConsumer is built by the {@link #build()}
+         * method.
+         * </p>
+         */
+        public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) {
+            if (mUidBatteryConsumers == null) {
+                mUidBatteryConsumers = new ArrayList<>();
+            }
+            mUidBatteryConsumers.add(uidBatteryConsumerBuilder);
+        }
+
+        /**
+         * Creates a read-only object out of the Builder values.
+         */
+        @NonNull
+        public UserBatteryConsumer build() {
+            if (mUidBatteryConsumers != null) {
+                for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
+                    UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i);
+                    mPowerComponentsBuilder.addPowerAndDuration(
+                            uidBatteryConsumer.mPowerComponentsBuilder);
+                }
+            }
+            return new UserBatteryConsumer(this);
+        }
+    }
+}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 7e50ebc..2119e7b 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -326,6 +326,19 @@
     }
 
     /**
+     * Returns the uid that is composed from the userHandle and the appId.
+     *
+     * @param userHandle the UserHandle to compose the uid
+     * @param appId the AppId to compose the uid
+     * @return the uid that is composed from the userHandle and the appId
+     * @hide
+     */
+    @SystemApi
+    public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+        return getUid(userHandle.getIdentifier(), appId);
+    }
+
+    /**
      * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
      * @hide
      */
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 7e7057f..0ff68fc 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -304,20 +304,16 @@
     }
 
     /**
-     * Called when a callback wants to stop listen to the loading progress of an installed package.
-     * Decrease the count of the callbacks on the associated to the corresponding storage.
-     * If the count becomes zero, unregister the storage listener.
+     * Called to stop all listeners from listening to loading progress of an installed package.
      * @param codePath Path of the installed package
-     * @return True if the package name and associated storage id are valid. False otherwise.
      */
-    public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
-            @NonNull IPackageLoadingProgressCallback callback) {
+    public void unregisterLoadingProgressCallbacks(@NonNull String codePath) {
         final IncrementalStorage storage = openStorage(codePath);
         if (storage == null) {
             // storage does not exist, package not installed
-            return false;
+            return;
         }
-        return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
+        mLoadingProgressCallbacks.cleanUpCallbacks(storage);
     }
 
     private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
@@ -325,7 +321,6 @@
         private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
                 new SparseArray<>();
 
-        // TODO(b/165841827): disable callbacks when app state changes to fully loaded
         public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
             final int storageId = storage.getId();
             final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index faa90d9..86b20c4 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -350,6 +350,9 @@
             return cachedTypeface;
         }
 
+        Log.w(TAG, "Platform version of downloadable fonts is deprecated. Please use"
+                + " androidx version instead.");
+
         synchronized (sLock) {
             // It is possible that Font is loaded during the thread sleep time
             // re-check the cache to avoid re-loading the font
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9603f4d..ef81ed7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -44,6 +44,7 @@
 import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -89,8 +90,11 @@
 import com.android.internal.widget.ILockSettings;
 
 import java.io.IOException;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -99,7 +103,6 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-
 /**
  * The Settings provider contains global system-level device preferences.
  */
@@ -2490,7 +2493,7 @@
     public static final String EXTRA_NUMBER_OF_CERTIFICATES =
             "android.settings.extra.number_of_certificates";
 
-    private static final String JID_RESOURCE_PREFIX = "android";
+    private static final String SYSTEM_PACKAGE_NAME = "android";
 
     public static final String AUTHORITY = "settings";
 
@@ -2659,22 +2662,30 @@
         private final String mCallListCommand;
         private final String mCallSetAllCommand;
 
+        private final ArraySet<String> mReadableFields;
+        private final ArraySet<String> mAllFields;
+
         @GuardedBy("this")
         private GenerationTracker mGenerationTracker;
 
-        public NameValueCache(Uri uri, String getCommand, String setCommand,
-                ContentProviderHolder providerHolder) {
-            this(uri, getCommand, setCommand, null, null, providerHolder);
+        <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
+                String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
+            this(uri, getCommand, setCommand, null, null, providerHolder,
+                    callerClass);
         }
 
-        NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
-                String setAllCommand, ContentProviderHolder providerHolder) {
+        private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
+                String setCommand, String listCommand, String setAllCommand,
+                ContentProviderHolder providerHolder, Class<T> callerClass) {
             mUri = uri;
             mCallGetCommand = getCommand;
             mCallSetCommand = setCommand;
             mCallListCommand = listCommand;
             mCallSetAllCommand = setAllCommand;
             mProviderHolder = providerHolder;
+            mReadableFields = new ArraySet<>();
+            mAllFields = new ArraySet<>();
+            getPublicSettingsForClass(callerClass, mAllFields, mReadableFields);
         }
 
         public boolean putStringForUser(ContentResolver cr, String name, String value,
@@ -2726,6 +2737,19 @@
 
         @UnsupportedAppUsage
         public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
+            // Check if the target settings key is readable. Reject if the caller is not system and
+            // is trying to access a settings key defined in the Settings.Secure, Settings.System or
+            // Settings.Global and is not annotated as @Readable.
+            // Notice that a key string that is not defined in any of the Settings.* classes will
+            // still be regarded as readable.
+            // TODO(b/175024829): provide a register method.
+            if (!Settings.isInSystemServer() && !isSystemOrPrivilegedApp()
+                    && mAllFields.contains(name) && !mReadableFields.contains(name)) {
+                throw new SecurityException(
+                        "Settings key: <" + name + "> is not readable. From S+, new public "
+                        + "settings keys need to be annotated with @Readable unless they are "
+                        + "annotated with @hide.");
+            }
             final boolean isSelf = (userHandle == UserHandle.myUserId());
             int currentGeneration = -1;
             if (isSelf) {
@@ -2900,6 +2924,19 @@
             }
         }
 
+        private static boolean isSystemOrPrivilegedApp() {
+            if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
+                return true;
+            }
+            final Application application = ActivityThread.currentApplication();
+            if (application == null || application.getApplicationInfo() == null) {
+                return false;
+            }
+            final ApplicationInfo applicationInfo = application.getApplicationInfo();
+            return applicationInfo.isSystemApp() || applicationInfo.isPrivilegedApp()
+                    || applicationInfo.isSignedWithPlatformKey();
+        }
+
         public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
                 List<String> names) {
             String namespace = prefix.substring(0, prefix.length() - 1);
@@ -3067,6 +3104,39 @@
     }
 
     /**
+     * This annotation indicates that the value of a setting is allowed to be read
+     * with the get* methods. The following settings should be readable:
+     * 1) all the public settings
+     * 2) all the hidden settings added before S
+     */
+    @Target({ ElementType.FIELD })
+    @Retention(RetentionPolicy.RUNTIME)
+    private @interface Readable {
+    }
+
+    private static <T extends NameValueTable> void getPublicSettingsForClass(
+            Class<T> callerClass, Set<String> allKeys, Set<String> readableKeys) {
+        final Field[] allFields = callerClass.getDeclaredFields();
+        try {
+            for (int i = 0; i < allFields.length; i++) {
+                final Field field = allFields[i];
+                if (!field.getType().equals(String.class)) {
+                    continue;
+                }
+                final Object value = field.get(callerClass);
+                if (!value.getClass().equals(String.class)) {
+                    continue;
+                }
+                allKeys.add((String) value);
+                if (field.getAnnotation(Readable.class) != null) {
+                    readableKeys.add((String) value);
+                }
+            }
+        } catch (IllegalAccessException ignored) {
+        }
+    }
+
+    /**
      * System settings, containing miscellaneous system preferences.  This
      * table holds simple name/value pairs.  There are convenience
      * functions for accessing individual settings entries.
@@ -3093,7 +3163,8 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SYSTEM,
                 CALL_METHOD_PUT_SYSTEM,
-                sProviderHolder);
+                sProviderHolder,
+                System.class);
 
         @UnsupportedAppUsage
         private static final HashSet<String> MOVED_TO_SECURE;
@@ -3147,8 +3218,11 @@
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.BLUETOOTH_ON);
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DATA_ROAMING);
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DEVICE_PROVISIONED);
-            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.HTTP_PROXY);
+            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.NETWORK_PREFERENCE);
+            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
+            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
+            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MAX_DHCP_RETRY_COUNT);
 
             // these are moving directly from system to global
             MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
@@ -3186,6 +3260,12 @@
             MOVED_TO_GLOBAL.add(Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL);
             MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_CONTENT_URL);
             MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL);
+            MOVED_TO_GLOBAL.add(Settings.Global.RADIO_NFC);
+            MOVED_TO_GLOBAL.add(Settings.Global.RADIO_CELL);
+            MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIFI);
+            MOVED_TO_GLOBAL.add(Settings.Global.RADIO_BLUETOOTH);
+            MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIMAX);
+            MOVED_TO_GLOBAL.add(Settings.Global.SHOW_PROCESSES);
         }
 
         /** @hide */
@@ -3210,6 +3290,11 @@
             sNameValueCache.clearGenerationTrackerForTest();
         }
 
+        /** @hide */
+        public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+            getPublicSettingsForClass(System.class, allKeys, readableKeys);
+        }
+
         /**
          * Look up a name in the database.
          * @param resolver to access the database with
@@ -3234,6 +3319,7 @@
                         + " to android.provider.Settings.Global, returning read-only value.");
                 return Global.getStringForUser(resolver, name, userHandle);
             }
+
             return sNameValueCache.getStringForUser(resolver, name, userHandle);
         }
 
@@ -3711,6 +3797,7 @@
          * 3 - The end button goes to the home screen.  If the user is already on the
          * home screen, it puts the device to sleep.
          */
+        @Readable
         public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
 
         /**
@@ -3735,6 +3822,7 @@
          * Is advanced settings mode turned on. 0 == no, 1 == yes
          * @hide
          */
+        @Readable
         public static final String ADVANCED_SETTINGS = "advanced_settings";
 
         /**
@@ -3835,6 +3923,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip";
 
         /**
@@ -3845,6 +3934,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_STATIC_IP = "wifi_static_ip";
 
         /**
@@ -3855,6 +3945,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway";
 
         /**
@@ -3865,6 +3956,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask";
 
         /**
@@ -3875,6 +3967,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1";
 
         /**
@@ -3885,6 +3978,7 @@
          * @deprecated Use {@link WifiManager} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2";
 
         /**
@@ -3895,6 +3989,7 @@
          * 1 -- connectable but not discoverable
          * 0 -- neither connectable nor discoverable
          */
+        @Readable
         public static final String BLUETOOTH_DISCOVERABILITY =
             "bluetooth_discoverability";
 
@@ -3903,6 +3998,7 @@
          * Bluetooth becomes discoverable for a certain number of seconds,
          * after which is becomes simply connectable.  The value is in seconds.
          */
+        @Readable
         public static final String BLUETOOTH_DISCOVERABILITY_TIMEOUT =
             "bluetooth_discoverability_timeout";
 
@@ -3936,11 +4032,13 @@
          * @deprecated Use {@link android.app.AlarmManager#getNextAlarmClock()}.
          */
         @Deprecated
+        @Readable
         public static final String NEXT_ALARM_FORMATTED = "next_alarm_formatted";
 
         /**
          * Scaling factor for fonts, float.
          */
+        @Readable
         public static final String FONT_SCALE = "font_scale";
 
         /**
@@ -3952,6 +4050,7 @@
          * instead.
          * @hide
          */
+        @Readable
         public static final String SYSTEM_LOCALES = "system_locales";
 
 
@@ -3977,12 +4076,14 @@
          * @deprecated This setting is no longer used.
          */
         @Deprecated
+        @Readable
         public static final String DIM_SCREEN = "dim_screen";
 
         /**
          * The display color mode.
          * @hide
          */
+        @Readable
         public static final String DISPLAY_COLOR_MODE = "display_color_mode";
 
         /**
@@ -3990,6 +4091,7 @@
          * unset or a match is not made, only the standard color modes will be restored.
          * @hide
          */
+        @Readable
         public static final String DISPLAY_COLOR_MODE_VENDOR_HINT =
                 "display_color_mode_vendor_hint";
 
@@ -3999,6 +4101,7 @@
          * If this isn't set, 0 will be used.
          * @hide
          */
+        @Readable
         public static final String MIN_REFRESH_RATE = "min_refresh_rate";
 
         /**
@@ -4007,6 +4110,7 @@
          * If this isn't set, the system falls back to a device specific default.
          * @hide
          */
+        @Readable
         public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
 
         /**
@@ -4019,23 +4123,27 @@
          * This value is bounded by maximum timeout set by
          * {@link android.app.admin.DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)}.
          */
+        @Readable
         public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
 
         /**
          * The screen backlight brightness between 0 and 255.
          */
+        @Readable
         public static final String SCREEN_BRIGHTNESS = "screen_brightness";
 
         /**
          * The screen backlight brightness between 0 and 255.
          * @hide
          */
+        @Readable
         public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
 
         /**
          * The screen backlight brightness between 0.0f and 1.0f.
          * @hide
          */
+        @Readable
         public static final String SCREEN_BRIGHTNESS_FOR_VR_FLOAT =
                 "screen_brightness_for_vr_float";
 
@@ -4043,11 +4151,13 @@
          * The screen backlight brightness between 0.0f and 1.0f.
          * @hide
          */
+        @Readable
         public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float";
 
         /**
          * Control whether to enable automatic brightness mode.
          */
+        @Readable
         public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
 
         /**
@@ -4056,6 +4166,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
 
         /**
@@ -4073,6 +4184,8 @@
          * @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead.
          * @hide
          */
+        @Deprecated
+        @Readable
         public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
 
         /**
@@ -4099,6 +4212,7 @@
          * stream type's bit should be set to 1 if it should be muted when going
          * into an inaudible ringer mode.
          */
+        @Readable
         public static final String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected";
 
         /**
@@ -4106,12 +4220,14 @@
           * stream type's bit should be set to 1 if it should be muted when a mute request
           * is received.
           */
+        @Readable
         public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected";
 
         /**
          * Whether vibrate is on for different events. This is used internally,
          * changing this value will not change the vibrate. See AudioManager.
          */
+        @Readable
         public static final String VIBRATE_ON = "vibrate_on";
 
         /**
@@ -4126,6 +4242,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
 
         /**
@@ -4142,6 +4259,7 @@
          * 3 - Strong vibrations
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_VIBRATION_INTENSITY =
                 "notification_vibration_intensity";
         /**
@@ -4158,6 +4276,7 @@
          * 3 - Strong vibrations
          * @hide
          */
+        @Readable
         public static final String RING_VIBRATION_INTENSITY =
                 "ring_vibration_intensity";
 
@@ -4175,6 +4294,7 @@
          * 3 - Strong vibrations
          * @hide
          */
+        @Readable
         public static final String HAPTIC_FEEDBACK_INTENSITY =
                 "haptic_feedback_intensity";
 
@@ -4184,6 +4304,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_RING = "volume_ring";
 
         /**
@@ -4192,6 +4313,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_SYSTEM = "volume_system";
 
         /**
@@ -4200,6 +4322,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_VOICE = "volume_voice";
 
         /**
@@ -4208,6 +4331,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_MUSIC = "volume_music";
 
         /**
@@ -4216,6 +4340,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_ALARM = "volume_alarm";
 
         /**
@@ -4224,6 +4349,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_NOTIFICATION = "volume_notification";
 
         /**
@@ -4232,6 +4358,7 @@
          *
          * @removed Not used by anything since API 2.
          */
+        @Readable
         public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
 
         /**
@@ -4239,12 +4366,14 @@
          * Acessibility volume. This is used internally, changing this
          * value will not change the volume.
          */
+        @Readable
         public static final String VOLUME_ACCESSIBILITY = "volume_a11y";
 
         /**
          * @hide
          * Volume index for virtual assistant.
          */
+        @Readable
         public static final String VOLUME_ASSISTANT = "volume_assistant";
 
         /**
@@ -4252,6 +4381,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String VOLUME_MASTER = "volume_master";
 
         /**
@@ -4260,6 +4390,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String MASTER_MONO = "master_mono";
 
         /**
@@ -4267,6 +4398,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MASTER_BALANCE = "master_balance";
 
         /**
@@ -4284,6 +4416,7 @@
          * @deprecated
          */
         @Deprecated
+        @Readable
         public static final String NOTIFICATIONS_USE_RING_VOLUME =
             "notifications_use_ring_volume";
 
@@ -4300,6 +4433,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String VIBRATE_IN_SILENT = "vibrate_in_silent";
 
         /**
@@ -4335,6 +4469,7 @@
          *
          * @removed  Not used by anything since API 2.
          */
+        @Readable
         public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
 
         /**
@@ -4346,6 +4481,7 @@
          *
          * @see #DEFAULT_RINGTONE_URI
          */
+        @Readable
         public static final String RINGTONE = "ringtone";
 
         /**
@@ -4359,6 +4495,7 @@
         public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
 
         /** {@hide} */
+        @Readable
         public static final String RINGTONE_CACHE = "ringtone_cache";
         /** {@hide} */
         public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
@@ -4369,6 +4506,7 @@
          * @see #RINGTONE
          * @see #DEFAULT_NOTIFICATION_URI
          */
+        @Readable
         public static final String NOTIFICATION_SOUND = "notification_sound";
 
         /**
@@ -4380,6 +4518,7 @@
         public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);
 
         /** {@hide} */
+        @Readable
         public static final String NOTIFICATION_SOUND_CACHE = "notification_sound_cache";
         /** {@hide} */
         public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE);
@@ -4390,6 +4529,7 @@
          * @see #RINGTONE
          * @see #DEFAULT_ALARM_ALERT_URI
          */
+        @Readable
         public static final String ALARM_ALERT = "alarm_alert";
 
         /**
@@ -4401,6 +4541,7 @@
         public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
 
         /** {@hide} */
+        @Readable
         public static final String ALARM_ALERT_CACHE = "alarm_alert_cache";
         /** {@hide} */
         public static final Uri ALARM_ALERT_CACHE_URI = getUriFor(ALARM_ALERT_CACHE);
@@ -4410,29 +4551,35 @@
          *
          * @hide
          */
+        @Readable
         public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
 
         /**
          * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
          */
+        @Readable
         public static final String TEXT_AUTO_REPLACE = "auto_replace";
 
         /**
          * Setting to enable Auto Caps in text editors. 1 = On, 0 = Off
          */
+        @Readable
         public static final String TEXT_AUTO_CAPS = "auto_caps";
 
         /**
          * Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This
          * feature converts two spaces to a "." and space.
          */
+        @Readable
         public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
 
         /**
          * Setting to showing password characters in text editors. 1 = On, 0 = Off
          */
+        @Readable
         public static final String TEXT_SHOW_PASSWORD = "show_password";
 
+        @Readable
         public static final String SHOW_GTALK_SERVICE_STATUS =
                 "SHOW_GTALK_SERVICE_STATUS";
 
@@ -4442,6 +4589,7 @@
          * @deprecated Use {@link WallpaperManager} instead.
          */
         @Deprecated
+        @Readable
         public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
 
         /**
@@ -4463,6 +4611,7 @@
          *   12
          *   24
          */
+        @Readable
         public static final String TIME_12_24 = "time_12_24";
 
         /**
@@ -4471,6 +4620,7 @@
          *   dd/mm/yyyy
          *   yyyy/mm/dd
          */
+        @Readable
         public static final String DATE_FORMAT = "date_format";
 
         /**
@@ -4480,6 +4630,7 @@
          * nonzero = it has been run in the past
          * 0 = it has not been run in the past
          */
+        @Readable
         public static final String SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run";
 
         /**
@@ -4516,6 +4667,7 @@
          * by the application; if 1, it will be used by default unless explicitly
          * disabled by the application.
          */
+        @Readable
         public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";
 
         /**
@@ -4526,6 +4678,7 @@
          *
          * @see Display#getRotation
          */
+        @Readable
         public static final String USER_ROTATION = "user_rotation";
 
         /**
@@ -4540,6 +4693,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY =
                 "hide_rotation_lock_toggle_for_accessibility";
 
@@ -4553,6 +4707,7 @@
          * relied on the setting, while this is purely about the vibration setting for incoming
          * calls.
          */
+        @Readable
         public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
 
         /**
@@ -4560,6 +4715,7 @@
          * {@code 0}, enhanced call blocking functionality is disabled.
          * @hide
          */
+        @Readable
         public static final String DEBUG_ENABLE_ENHANCED_CALL_BLOCKING =
                 "debug.enable_enhanced_calling";
 
@@ -4567,6 +4723,7 @@
          * Whether the audible DTMF tones are played by the dialer when dialing. The value is
          * boolean (1 or 0).
          */
+        @Readable
         public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
 
         /**
@@ -4575,6 +4732,7 @@
          *                 0 = Normal
          *                 1 = Long
          */
+        @Readable
         public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
 
         /**
@@ -4583,6 +4741,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String HEARING_AID = "hearing_aid";
 
         /**
@@ -4595,18 +4754,21 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String TTY_MODE = "tty_mode";
 
         /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
          * boolean (1 or 0).
          */
+        @Readable
         public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
 
         /**
          * Whether haptic feedback (Vibrate on tap) is enabled. The value is
          * boolean (1 or 0).
          */
+        @Readable
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
 
         /**
@@ -4614,6 +4776,7 @@
          * setting for this.
          */
         @Deprecated
+        @Readable
         public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
 
         /**
@@ -4622,6 +4785,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
 
         /**
@@ -4631,6 +4795,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String POINTER_LOCATION = "pointer_location";
 
         /**
@@ -4640,6 +4805,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String SHOW_TOUCHES = "show_touches";
 
         /**
@@ -4650,6 +4816,7 @@
          * 1 = yes
          * @hide
          */
+        @Readable
         public static final String WINDOW_ORIENTATION_LISTENER_LOG =
                 "window_orientation_listener_log";
 
@@ -4675,12 +4842,14 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled";
 
         /**
          * Whether the lockscreen should be completely disabled.
          * @hide
          */
+        @Readable
         public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled";
 
         /**
@@ -4751,6 +4920,7 @@
          * 1 = yes
          * @hide
          */
+        @Readable
         public static final String SIP_RECEIVE_CALLS = "sip_receive_calls";
 
         /**
@@ -4759,18 +4929,21 @@
          * "SIP_ADDRESS_ONLY" : Only if destination is a SIP address
          * @hide
          */
+        @Readable
         public static final String SIP_CALL_OPTIONS = "sip_call_options";
 
         /**
          * One of the sip call options: Always use SIP with network access.
          * @hide
          */
+        @Readable
         public static final String SIP_ALWAYS = "SIP_ALWAYS";
 
         /**
          * One of the sip call options: Only if destination is a SIP address.
          * @hide
          */
+        @Readable
         public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY";
 
         /**
@@ -4781,6 +4954,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME";
 
         /**
@@ -4792,12 +4966,14 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String POINTER_SPEED = "pointer_speed";
 
         /**
          * Whether lock-to-app will be triggered by long-press on recents.
          * @hide
          */
+        @Readable
         public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled";
 
         /**
@@ -4807,6 +4983,7 @@
          * Backward-compatible with <code>PrefGetPreference(prefAllowEasterEggs)</code>.
          * @hide
          */
+        @Readable
         public static final String EGG_MODE = "egg_mode";
 
         /**
@@ -4815,6 +4992,7 @@
          *    1 - Show percentage
          * @hide
          */
+        @Readable
         public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
 
         /**
@@ -4823,6 +5001,7 @@
          * for instance pausing media apps when another starts.
          * @hide
          */
+        @Readable
         public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled";
 
         /**
@@ -5017,6 +5196,7 @@
          * @see android.telephony.TelephonyManager.WifiCallingChoices
          * @hide
          */
+        @Readable
         public static final String WHEN_TO_MAKE_WIFI_CALLS = "when_to_make_wifi_calls";
 
         // Settings moved to Settings.Secure
@@ -5173,6 +5353,7 @@
          * instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
                 Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE;
 
@@ -5187,6 +5368,7 @@
          * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
                 Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS;
 
@@ -5195,6 +5377,7 @@
          * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
                 Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED;
 
@@ -5204,6 +5387,7 @@
          * instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
                 Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
 
@@ -5212,6 +5396,7 @@
          * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
             Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT;
 
@@ -5246,6 +5431,7 @@
          * instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
             Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
 
@@ -5295,7 +5481,8 @@
                 CONTENT_URI,
                 CALL_METHOD_GET_SECURE,
                 CALL_METHOD_PUT_SECURE,
-                sProviderHolder);
+                sProviderHolder,
+                Secure.class);
 
         private static ILockSettings sLockSettings = null;
 
@@ -5433,6 +5620,11 @@
             sNameValueCache.clearGenerationTrackerForTest();
         }
 
+        /** @hide */
+        public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+            getPublicSettingsForClass(Secure.class, allKeys, readableKeys);
+        }
+
         /**
          * Look up a name in the database.
          * @param resolver to access the database with
@@ -5928,6 +6120,7 @@
          * Control whether to enable adaptive sleep mode.
          * @hide
          */
+        @Readable
         public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
 
         /**
@@ -5935,6 +6128,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAMERA_AUTOROTATE = "camera_autorotate";
 
         /**
@@ -5952,6 +6146,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
 
         /**
@@ -5969,6 +6164,7 @@
          * @deprecated This settings is not used anymore.
          */
         @Deprecated
+        @Readable
         public static final String ALLOW_MOCK_LOCATION = "mock_location";
 
         /**
@@ -5977,6 +6173,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
 
         /**
@@ -6016,6 +6213,7 @@
          * to the Instant App, it is generated when the Instant App is first installed and reset if
          * the user clears the Instant App.
          */
+        @Readable
         public static final String ANDROID_ID = "android_id";
 
         /**
@@ -6034,12 +6232,14 @@
          * Setting to record the input method used by default, holding the ID
          * of the desired method.
          */
+        @Readable
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
          * Setting to record the input method subtype used by default, holding the ID
          * of the desired method.
          */
+        @Readable
         public static final String SELECTED_INPUT_METHOD_SUBTYPE =
                 "selected_input_method_subtype";
 
@@ -6048,12 +6248,14 @@
          * and its last used subtype.
          * @hide
          */
+        @Readable
         public static final String INPUT_METHODS_SUBTYPE_HISTORY =
                 "input_methods_subtype_history";
 
         /**
          * Setting to record the visibility of input method selector
          */
+        @Readable
         public static final String INPUT_METHOD_SELECTOR_VISIBILITY =
                 "input_method_selector_visibility";
 
@@ -6062,6 +6264,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
 
         /**
@@ -6069,6 +6272,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
         /**
@@ -6079,6 +6283,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
                 "autofill_field_classification";
 
@@ -6087,6 +6292,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DARK_MODE_DIALOG_SEEN =
                 "dark_mode_dialog_seen";
 
@@ -6095,6 +6301,7 @@
          * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
          * @hide
          */
+        @Readable
         public static final String DARK_THEME_CUSTOM_START_TIME =
                 "dark_theme_custom_start_time";
 
@@ -6103,6 +6310,7 @@
          * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
          * @hide
          */
+        @Readable
         public static final String DARK_THEME_CUSTOM_END_TIME =
                 "dark_theme_custom_end_time";
 
@@ -6112,6 +6320,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
                 "autofill_user_data_max_user_data_size";
 
@@ -6122,6 +6331,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
                 "autofill_user_data_max_field_classification_size";
 
@@ -6132,6 +6342,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
                 "autofill_user_data_max_category_count";
 
@@ -6141,6 +6352,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
                 "autofill_user_data_max_value_length";
 
@@ -6150,6 +6362,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
                 "autofill_user_data_min_value_length";
 
@@ -6162,6 +6375,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
 
         /**
@@ -6179,6 +6393,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MANAGED_PROVISIONING_DPC_DOWNLOADED =
                 "managed_provisioning_dpc_downloaded";
 
@@ -6189,6 +6404,7 @@
          * <p>
          * Type: int (0 for false, 1 for true)
          */
+        @Readable
         public static final String SECURE_FRP_MODE = "secure_frp_mode";
 
         /**
@@ -6199,6 +6415,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String USER_SETUP_COMPLETE = "user_setup_complete";
 
         /**
@@ -6254,6 +6471,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String USER_SETUP_PERSONALIZATION_STATE =
                 "user_setup_personalization_state";
 
@@ -6264,6 +6482,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
 
         /**
@@ -6275,6 +6494,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
 
         /**
@@ -6285,6 +6505,7 @@
          * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
          * where imeId is ComponentName and subtype is int32.
          */
+        @Readable
         public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
 
         /**
@@ -6293,6 +6514,7 @@
          * by ':'.
          * @hide
          */
+        @Readable
         public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
 
         /**
@@ -6301,6 +6523,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
 
@@ -6318,6 +6541,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app";
 
         /**
@@ -6326,6 +6550,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
 
         /**
@@ -6335,6 +6560,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST =
                 "always_on_vpn_lockdown_whitelist";
 
@@ -6348,6 +6574,8 @@
          * {@link PackageManager#canRequestPackageInstalls()}
          * @see PackageManager#canRequestPackageInstalls()
          */
+        @Deprecated
+        @Readable
         public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
 
         /**
@@ -6359,6 +6587,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED =
                 "unknown_sources_default_reversed";
 
@@ -6372,6 +6601,7 @@
          * instead.
          */
         @Deprecated
+        @Readable
         public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
 
         /**
@@ -6383,12 +6613,14 @@
          * {@link LocationManager#MODE_CHANGED_ACTION}.
          */
         @Deprecated
+        @Readable
         public static final String LOCATION_MODE = "location_mode";
 
         /**
          * The App or module that changes the location mode.
          * @hide
          */
+        @Readable
         public static final String LOCATION_CHANGER = "location_changer";
 
         /**
@@ -6457,6 +6689,7 @@
          * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update.
          * @hide
          */
+        @Readable
         public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED =
                 "location_time_zone_detection_enabled";
 
@@ -6466,6 +6699,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
 
         /**
@@ -6473,6 +6707,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String LOCK_BIOMETRIC_WEAK_FLAGS =
                 "lock_biometric_weak_flags";
 
@@ -6480,6 +6715,7 @@
          * Whether lock-to-app will lock the keyguard when exiting.
          * @hide
          */
+        @Readable
         public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked";
 
         /**
@@ -6490,6 +6726,7 @@
          *             {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
          */
         @Deprecated
+        @Readable
         public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
 
         /**
@@ -6499,6 +6736,7 @@
          *             {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
          */
         @Deprecated
+        @Readable
         public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
 
         /**
@@ -6512,6 +6750,7 @@
          *             {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
          */
         @Deprecated
+        @Readable
         public static final String
                 LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
 
@@ -6521,6 +6760,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout";
 
 
@@ -6530,6 +6770,7 @@
          * @deprecated
          */
         @Deprecated
+        @Readable
         public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
 
         /**
@@ -6537,6 +6778,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String LOCK_SCREEN_APPWIDGET_IDS =
             "lock_screen_appwidget_ids";
 
@@ -6545,6 +6787,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String LOCK_SCREEN_FALLBACK_APPWIDGET_ID =
             "lock_screen_fallback_appwidget_id";
 
@@ -6553,6 +6796,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String LOCK_SCREEN_STICKY_APPWIDGET =
             "lock_screen_sticky_appwidget";
 
@@ -6563,6 +6807,7 @@
          */
         @Deprecated
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
             "lock_screen_owner_info_enabled";
 
@@ -6575,6 +6820,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
                 "lock_screen_allow_private_notifications";
 
@@ -6583,6 +6829,7 @@
          * without having to unlock
          * @hide
          */
+        @Readable
         public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT =
                 "lock_screen_allow_remote_input";
 
@@ -6592,12 +6839,14 @@
          *     {"clock": id, "_applied_timestamp": timestamp}
          * @hide
          */
+        @Readable
         public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
 
         /**
          * Indicates which clock face to show on lock screen and AOD while docked.
          * @hide
          */
+        @Readable
         public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
 
         /**
@@ -6605,6 +6854,7 @@
          * the lockscreen notification policy.
          * @hide
          */
+        @Readable
         public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
                 "show_note_about_notification_hiding";
 
@@ -6612,6 +6862,7 @@
          * Set to 1 by the system after trust agents have been initialized.
          * @hide
          */
+        @Readable
         public static final String TRUST_AGENTS_INITIALIZED =
                 "trust_agents_initialized";
 
@@ -6622,6 +6873,7 @@
          * many collisions.  It should not be used.
          */
         @Deprecated
+        @Readable
         public static final String LOGGING_ID = "logging_id";
 
         /**
@@ -6633,16 +6885,19 @@
         /**
          * No longer supported.
          */
+        @Readable
         public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
 
         /**
          * No longer supported.
          */
+        @Readable
         public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
 
         /**
          * No longer supported.
          */
+        @Readable
         public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
 
         /**
@@ -6651,6 +6906,7 @@
          * and new Settings apps.
          */
         // TODO: 881807
+        @Readable
         public static final String SETTINGS_CLASSNAME = "settings_classname";
 
         /**
@@ -6668,12 +6924,14 @@
         /**
          * If accessibility is enabled.
          */
+        @Readable
         public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
 
         /**
          * Setting specifying if the accessibility shortcut is enabled.
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
                 "accessibility_shortcut_on_lock_screen";
 
@@ -6681,6 +6939,7 @@
          * Setting specifying if the accessibility shortcut dialog has been shown to this user.
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
                 "accessibility_shortcut_dialog_shown";
 
@@ -6695,6 +6954,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
                 "accessibility_shortcut_target_service";
 
@@ -6705,6 +6965,7 @@
          * accessibility feature.
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
                 "accessibility_button_target_component";
 
@@ -6717,6 +6978,7 @@
          * accessibility feature.
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
 
         /**
@@ -6725,17 +6987,20 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER =
                 "com.android.server.accessibility.MagnificationController";
 
         /**
          * If touch exploration is enabled.
          */
+        @Readable
         public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
 
         /**
          * List of the enabled accessibility providers.
          */
+        @Readable
         public static final String ENABLED_ACCESSIBILITY_SERVICES =
             "enabled_accessibility_services";
 
@@ -6745,6 +7010,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
             "touch_exploration_granted_accessibility_services";
 
@@ -6752,12 +7018,14 @@
          * Whether the Global Actions Panel is enabled.
          * @hide
          */
+        @Readable
         public static final String GLOBAL_ACTIONS_PANEL_ENABLED = "global_actions_panel_enabled";
 
         /**
          * Whether the Global Actions Panel can be toggled on or off in Settings.
          * @hide
          */
+        @Readable
         public static final String GLOBAL_ACTIONS_PANEL_AVAILABLE =
                 "global_actions_panel_available";
 
@@ -6765,6 +7033,7 @@
          * Enables debug mode for the Global Actions Panel.
          * @hide
          */
+        @Readable
         public static final String GLOBAL_ACTIONS_PANEL_DEBUG_ENABLED =
                 "global_actions_panel_debug_enabled";
 
@@ -6773,24 +7042,28 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String HUSH_GESTURE_USED = "hush_gesture_used";
 
         /**
          * Number of times the user has manually clicked the ringer toggle
          * @hide
          */
+        @Readable
         public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
 
         /**
          * Whether to play a sound for charging events.
          * @hide
          */
+        @Readable
         public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
 
         /**
          * Whether to vibrate for charging events.
          * @hide
          */
+        @Readable
         public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
 
         /**
@@ -6800,6 +7073,7 @@
          * user to specify a duration.
          * @hide
          */
+        @Readable
         public static final String ZEN_DURATION = "zen_duration";
 
         /** @hide */ public static final int ZEN_DURATION_PROMPT = -1;
@@ -6809,24 +7083,28 @@
          * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
          * @hide
          */
+        @Readable
         public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
 
         /**
          * If nonzero, will show the zen update settings suggestion.
          * @hide
          */
+        @Readable
         public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
 
         /**
          * If nonzero, zen has not been updated to reflect new changes.
          * @hide
          */
+        @Readable
         public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
 
         /**
          * If nonzero, zen setting suggestion has been viewed by user
          * @hide
          */
+        @Readable
         public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
                 "zen_settings_suggestion_viewed";
 
@@ -6835,6 +7113,7 @@
          * boolean (1 or 0).
          * @hide
          */
+        @Readable
         public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled";
 
         /**
@@ -6843,6 +7122,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri";
 
         /**
@@ -6855,6 +7135,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String FONT_WEIGHT_ADJUSTMENT = "font_weight_adjustment";
 
         /**
@@ -6865,6 +7146,7 @@
          * at all times, which was the behavior when this value was {@code true}.
          */
         @Deprecated
+        @Readable
         public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
 
         /**
@@ -6872,6 +7154,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
                 "high_text_contrast_enabled";
 
@@ -6885,6 +7168,7 @@
          */
         @UnsupportedAppUsage
         @TestApi
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
                 "accessibility_display_magnification_enabled";
 
@@ -6900,6 +7184,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
                 "accessibility_display_magnification_navbar_enabled";
 
@@ -6913,6 +7198,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
                 "accessibility_display_magnification_scale";
 
@@ -6923,6 +7209,7 @@
          * @deprecated
          */
         @Deprecated
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE =
                 "accessibility_display_magnification_auto_update";
 
@@ -6932,6 +7219,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_SOFT_KEYBOARD_MODE =
                 "accessibility_soft_keyboard_mode";
 
@@ -6965,6 +7253,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
                 "accessibility_captioning_enabled";
 
@@ -6975,6 +7264,7 @@
          * @see java.util.Locale#toString
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
                 "accessibility_captioning_locale";
 
@@ -6989,6 +7279,7 @@
          * @see java.util.Locale#toString
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_PRESET =
                 "accessibility_captioning_preset";
 
@@ -6999,6 +7290,7 @@
          * @see android.graphics.Color#argb
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
                 "accessibility_captioning_background_color";
 
@@ -7009,6 +7301,7 @@
          * @see android.graphics.Color#argb
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
                 "accessibility_captioning_foreground_color";
 
@@ -7023,6 +7316,7 @@
          * @see #ACCESSIBILITY_CAPTIONING_EDGE_COLOR
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
                 "accessibility_captioning_edge_type";
 
@@ -7034,6 +7328,7 @@
          * @see android.graphics.Color#argb
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
                 "accessibility_captioning_edge_color";
 
@@ -7044,6 +7339,7 @@
          * @see android.graphics.Color#argb
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
                 "accessibility_captioning_window_color";
 
@@ -7060,6 +7356,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
                 "accessibility_captioning_typeface";
 
@@ -7068,12 +7365,14 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
                 "accessibility_captioning_font_scale";
 
         /**
          * Setting that specifies whether display color inversion is enabled.
          */
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
                 "accessibility_display_inversion_enabled";
 
@@ -7084,6 +7383,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
                 "accessibility_display_daltonizer_enabled";
 
@@ -7100,6 +7400,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
                 "accessibility_display_daltonizer";
 
@@ -7110,6 +7411,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
                 "accessibility_autoclick_enabled";
 
@@ -7120,6 +7422,7 @@
          * @see #ACCESSIBILITY_AUTOCLICK_ENABLED
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
                 "accessibility_autoclick_delay";
 
@@ -7130,6 +7433,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
                 "accessibility_large_pointer_icon";
 
@@ -7138,6 +7442,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
 
         /**
@@ -7145,6 +7450,7 @@
          * down event for an interaction to be considered part of the same multi-press.
          * @hide
          */
+        @Readable
         public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout";
 
         /**
@@ -7153,6 +7459,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS =
                 "accessibility_non_interactive_ui_timeout_ms";
 
@@ -7162,6 +7469,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS =
                 "accessibility_interactive_ui_timeout_ms";
 
@@ -7172,6 +7480,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
                 "reduce_bright_colors_activated";
 
@@ -7181,6 +7490,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String REDUCE_BRIGHT_COLORS_LEVEL =
                 "reduce_bright_colors_level";
 
@@ -7189,6 +7499,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
                 "reduce_bright_colors_persist_across_reboots";
 
@@ -7201,6 +7512,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ENABLED_PRINT_SERVICES =
             "enabled_print_services";
 
@@ -7210,6 +7522,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String DISABLED_PRINT_SERVICES =
             "disabled_print_services";
 
@@ -7220,6 +7533,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
 
         /**
@@ -7232,21 +7546,25 @@
          * the framework text to speech APIs as of the Ice Cream Sandwich release.
          */
         @Deprecated
+        @Readable
         public static final String TTS_USE_DEFAULTS = "tts_use_defaults";
 
         /**
          * Default text-to-speech engine speech rate. 100 = 1x
          */
+        @Readable
         public static final String TTS_DEFAULT_RATE = "tts_default_rate";
 
         /**
          * Default text-to-speech engine pitch. 100 = 1x
          */
+        @Readable
         public static final String TTS_DEFAULT_PITCH = "tts_default_pitch";
 
         /**
          * Default text-to-speech engine.
          */
+        @Readable
         public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
 
         /**
@@ -7258,6 +7576,7 @@
          * locale. {@link TextToSpeech#getLanguage()}.
          */
         @Deprecated
+        @Readable
         public static final String TTS_DEFAULT_LANG = "tts_default_lang";
 
         /**
@@ -7269,6 +7588,7 @@
          * locale. {@link TextToSpeech#getLanguage()}.
          */
         @Deprecated
+        @Readable
         public static final String TTS_DEFAULT_COUNTRY = "tts_default_country";
 
         /**
@@ -7280,6 +7600,7 @@
          * locale that is in use {@link TextToSpeech#getLanguage()}.
          */
         @Deprecated
+        @Readable
         public static final String TTS_DEFAULT_VARIANT = "tts_default_variant";
 
         /**
@@ -7294,11 +7615,13 @@
          *
          * @hide
          */
+        @Readable
         public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
 
         /**
          * Space delimited list of plugin packages that are enabled.
          */
+        @Readable
         public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
 
         /**
@@ -7338,6 +7661,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
                 "wifi_watchdog_acceptable_packet_loss_percentage";
 
@@ -7347,6 +7671,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count";
 
         /**
@@ -7354,6 +7679,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
                 "wifi_watchdog_background_check_delay_ms";
 
@@ -7363,6 +7689,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
                 "wifi_watchdog_background_check_enabled";
 
@@ -7371,6 +7698,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
                 "wifi_watchdog_background_check_timeout_ms";
 
@@ -7382,6 +7710,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
             "wifi_watchdog_initial_ignored_ping_count";
 
@@ -7393,12 +7722,14 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks";
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_WATCHDOG_ON} instead
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
 
         /**
@@ -7406,6 +7737,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list";
 
         /**
@@ -7413,6 +7745,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count";
 
         /**
@@ -7420,6 +7753,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms";
 
         /**
@@ -7427,6 +7761,7 @@
          * @deprecated This setting is not used.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
 
         /**
@@ -7451,6 +7786,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
                 "connectivity_release_pending_intent_delay_ms";
 
@@ -7464,12 +7800,14 @@
          *             now appear disconnected.
          */
         @Deprecated
+        @Readable
         public static final String BACKGROUND_DATA = "background_data";
 
         /**
          * Origins for which browsers should allow geolocation by default.
          * The value is a space-separated list of origins.
          */
+        @Readable
         public static final String ALLOWED_GEOLOCATION_ORIGINS
                 = "allowed_geolocation_origins";
 
@@ -7480,6 +7818,7 @@
          *                            3 = TTY VCO
          * @hide
          */
+        @Readable
         public static final String PREFERRED_TTY_MODE =
                 "preferred_tty_mode";
 
@@ -7489,6 +7828,7 @@
          * 1 = enhanced voice privacy
          * @hide
          */
+        @Readable
         public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
 
         /**
@@ -7497,6 +7837,7 @@
          * 1 = enabled
          * @hide
          */
+        @Readable
         public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
 
         /**
@@ -7505,6 +7846,7 @@
          * 0 = OFF
          * 1 = ON
          */
+        @Readable
         public static final String RTT_CALLING_MODE = "rtt_calling_mode";
 
         /**
@@ -7514,6 +7856,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String BACKUP_ENABLED = "backup_enabled";
 
         /**
@@ -7523,6 +7866,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore";
 
         /**
@@ -7531,6 +7875,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String BACKUP_PROVISIONED = "backup_provisioned";
 
         /**
@@ -7538,6 +7883,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String BACKUP_TRANSPORT = "backup_transport";
 
         /**
@@ -7547,6 +7893,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String LAST_SETUP_SHOWN = "last_setup_shown";
 
         /**
@@ -7569,6 +7916,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SEARCH_GLOBAL_SEARCH_ACTIVITY =
                 "search_global_search_activity";
 
@@ -7576,21 +7924,25 @@
          * The number of promoted sources in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_NUM_PROMOTED_SOURCES = "search_num_promoted_sources";
         /**
          * The maximum number of suggestions returned by GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MAX_RESULTS_TO_DISPLAY = "search_max_results_to_display";
         /**
          * The number of suggestions GlobalSearch will ask each non-web search source for.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MAX_RESULTS_PER_SOURCE = "search_max_results_per_source";
         /**
          * The number of suggestions the GlobalSearch will ask the web search source for.
          * @hide
          */
+        @Readable
         public static final String SEARCH_WEB_RESULTS_OVERRIDE_LIMIT =
                 "search_web_results_override_limit";
         /**
@@ -7598,69 +7950,81 @@
          * promoted sources before continuing with all other sources.
          * @hide
          */
+        @Readable
         public static final String SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS =
                 "search_promoted_source_deadline_millis";
         /**
          * The number of milliseconds before GlobalSearch aborts search suggesiton queries.
          * @hide
          */
+        @Readable
         public static final String SEARCH_SOURCE_TIMEOUT_MILLIS = "search_source_timeout_millis";
         /**
          * The maximum number of milliseconds that GlobalSearch shows the previous results
          * after receiving a new query.
          * @hide
          */
+        @Readable
         public static final String SEARCH_PREFILL_MILLIS = "search_prefill_millis";
         /**
          * The maximum age of log data used for shortcuts in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MAX_STAT_AGE_MILLIS = "search_max_stat_age_millis";
         /**
          * The maximum age of log data used for source ranking in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS =
                 "search_max_source_event_age_millis";
         /**
          * The minimum number of impressions needed to rank a source in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING =
                 "search_min_impressions_for_source_ranking";
         /**
          * The minimum number of clicks needed to rank a source in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING =
                 "search_min_clicks_for_source_ranking";
         /**
          * The maximum number of shortcuts shown by GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_MAX_SHORTCUTS_RETURNED = "search_max_shortcuts_returned";
         /**
          * The size of the core thread pool for suggestion queries in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_QUERY_THREAD_CORE_POOL_SIZE =
                 "search_query_thread_core_pool_size";
         /**
          * The maximum size of the thread pool for suggestion queries in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_QUERY_THREAD_MAX_POOL_SIZE =
                 "search_query_thread_max_pool_size";
         /**
          * The size of the core thread pool for shortcut refreshing in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE =
                 "search_shortcut_refresh_core_pool_size";
         /**
          * The maximum size of the thread pool for shortcut refreshing in GlobalSearch.
          * @hide
          */
+        @Readable
         public static final String SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE =
                 "search_shortcut_refresh_max_pool_size";
         /**
@@ -7668,12 +8032,14 @@
          * wait before terminating.
          * @hide
          */
+        @Readable
         public static final String SEARCH_THREAD_KEEPALIVE_SECONDS =
                 "search_thread_keepalive_seconds";
         /**
          * The maximum number of concurrent suggestion queries to each source.
          * @hide
          */
+        @Readable
         public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT =
                 "search_per_source_concurrent_query_limit";
 
@@ -7682,24 +8048,28 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd";
 
         /**
          * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart";
 
         /**
          * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt";
 
         /**
          * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
 
         /**
@@ -7711,6 +8081,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
 
@@ -7720,6 +8091,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
                 "show_first_crash_dialog_dev_option";
@@ -7731,6 +8103,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
 
         /**
@@ -7741,6 +8114,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
 
@@ -7753,6 +8127,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
                 "selected_spell_checker_subtype";
@@ -7762,6 +8137,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
 
         /**
@@ -7774,6 +8150,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
 
         /**
@@ -7788,6 +8165,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MINIMAL_POST_PROCESSING_ALLOWED =
                 "minimal_post_processing_allowed";
 
@@ -7832,6 +8210,7 @@
          * @see #MATCH_CONTENT_FRAMERATE_ALWAYS
          * @hide
          */
+        @Readable
         public static final String MATCH_CONTENT_FRAME_RATE =
                 "match_content_frame_rate";
 
@@ -7863,6 +8242,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String INCALL_BACK_BUTTON_BEHAVIOR = "incall_back_button_behavior";
 
         /**
@@ -7888,6 +8268,7 @@
          * Whether the device should wake when the wake gesture sensor detects motion.
          * @hide
          */
+        @Readable
         public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
 
         /**
@@ -7895,6 +8276,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String DOZE_ENABLED = "doze_enabled";
 
         /**
@@ -7905,36 +8287,42 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String DOZE_ALWAYS_ON = "doze_always_on";
 
         /**
          * Whether the device should pulse on pick up gesture.
          * @hide
          */
+        @Readable
         public static final String DOZE_PICK_UP_GESTURE = "doze_pulse_on_pick_up";
 
         /**
          * Whether the device should pulse on long press gesture.
          * @hide
          */
+        @Readable
         public static final String DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press";
 
         /**
          * Whether the device should pulse on double tap gesture.
          * @hide
          */
+        @Readable
         public static final String DOZE_DOUBLE_TAP_GESTURE = "doze_pulse_on_double_tap";
 
         /**
          * Whether the device should respond to the SLPI tap gesture.
          * @hide
          */
+        @Readable
         public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture";
 
         /**
          * Gesture that wakes up the display, showing some version of the lock screen.
          * @hide
          */
+        @Readable
         public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_screen_gesture";
 
         /**
@@ -7942,84 +8330,98 @@
          * {@link Display.STATE_DOZE}.
          * @hide
          */
+        @Readable
         public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
 
         /**
          * Whether the device should suppress the current doze configuration and disable dozing.
          * @hide
          */
+        @Readable
         public static final String SUPPRESS_DOZE = "suppress_doze";
 
         /**
          * Gesture that skips media.
          * @hide
          */
+        @Readable
         public static final String SKIP_GESTURE = "skip_gesture";
 
         /**
          * Count of successful gestures.
          * @hide
          */
+        @Readable
         public static final String SKIP_GESTURE_COUNT = "skip_gesture_count";
 
         /**
          * Count of non-gesture interaction.
          * @hide
          */
+        @Readable
         public static final String SKIP_TOUCH_COUNT = "skip_touch_count";
 
         /**
          * Direction to advance media for skip gesture
          * @hide
          */
+        @Readable
         public static final String SKIP_DIRECTION = "skip_gesture_direction";
 
         /**
          * Gesture that silences sound (alarms, notification, calls).
          * @hide
          */
+        @Readable
         public static final String SILENCE_GESTURE = "silence_gesture";
 
         /**
          * Count of successful silence alarms gestures.
          * @hide
          */
+        @Readable
         public static final String SILENCE_ALARMS_GESTURE_COUNT = "silence_alarms_gesture_count";
 
         /**
          * Count of successful silence timer gestures.
          * @hide
          */
+        @Readable
         public static final String SILENCE_TIMER_GESTURE_COUNT = "silence_timer_gesture_count";
 
         /**
          * Count of successful silence call gestures.
          * @hide
          */
+        @Readable
         public static final String SILENCE_CALL_GESTURE_COUNT = "silence_call_gesture_count";
 
         /**
          * Count of non-gesture interaction.
          * @hide
          */
+        @Readable
         public static final String SILENCE_ALARMS_TOUCH_COUNT = "silence_alarms_touch_count";
 
         /**
          * Count of non-gesture interaction.
          * @hide
          */
+        @Readable
         public static final String SILENCE_TIMER_TOUCH_COUNT = "silence_timer_touch_count";
 
         /**
          * Count of non-gesture interaction.
          * @hide
          */
+        @Readable
         public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count";
 
         /**
          * Number of successful "Motion Sense" tap gestures to pause media.
          * @hide
          */
+        @Readable
         public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
 
         /**
@@ -8027,12 +8429,14 @@
          * have been used.
          * @hide
          */
+        @Readable
         public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
 
         /**
          * For user preference if swipe bottom to expand notification gesture enabled.
          * @hide
          */
+        @Readable
         public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
                 "swipe_bottom_to_notification_enabled";
 
@@ -8040,24 +8444,28 @@
          * For user preference if One-Handed Mode enabled.
          * @hide
          */
+        @Readable
         public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
 
         /**
          * For user preference if One-Handed Mode timeout.
          * @hide
          */
+        @Readable
         public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
 
         /**
          * For user taps app to exit One-Handed Mode.
          * @hide
          */
+        @Readable
         public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
 
         /**
          * Internal use, one handed mode tutorial showed times.
          * @hide
          */
+        @Readable
         public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT =
                 "one_handed_tutorial_show_count";
 
@@ -8067,6 +8475,7 @@
          * UiModeManager.
          * @hide
          */
+        @Readable
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         /**
@@ -8075,12 +8484,14 @@
          * UiModeManager.
          * @hide
          */
+        @Readable
         public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on";
 
         /**
          * The last computed night mode bool the last time the phone was on
          * @hide
          */
+        @Readable
         public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed";
 
         /**
@@ -8089,12 +8500,14 @@
          * UiModeManager.
          * @hide
          */
+        @Readable
         public static final String UI_NIGHT_MODE_OVERRIDE_OFF = "ui_night_mode_override_off";
 
         /**
          * Whether screensavers are enabled.
          * @hide
          */
+        @Readable
         public static final String SCREENSAVER_ENABLED = "screensaver_enabled";
 
         /**
@@ -8104,6 +8517,7 @@
          * battery, or upon dock insertion (if SCREENSAVER_ACTIVATE_ON_DOCK is set to 1).
          * @hide
          */
+        @Readable
         public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
 
         /**
@@ -8111,6 +8525,7 @@
          * when the device is inserted into a (desk) dock.
          * @hide
          */
+        @Readable
         public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock";
 
         /**
@@ -8118,12 +8533,14 @@
          * when the screen times out when not on battery.
          * @hide
          */
+        @Readable
         public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep";
 
         /**
          * If screensavers are enabled, the default screensaver component.
          * @hide
          */
+        @Readable
         public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
 
         /**
@@ -8132,12 +8549,14 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
         /**
          * Whether NFC payment is handled by the foreground application or a default.
          * @hide
          */
+        @Readable
         public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
 
         /**
@@ -8145,6 +8564,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
 
         /**
@@ -8152,6 +8572,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
 
         /**
@@ -8159,6 +8580,7 @@
          * application
          * @hide
          */
+        @Readable
         public static final String CALL_SCREENING_DEFAULT_COMPONENT =
                 "call_screening_default_component";
 
@@ -8169,6 +8591,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
 
         /**
@@ -8177,6 +8600,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
 
         /**
@@ -8185,6 +8609,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
 
         /**
@@ -8196,6 +8621,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled";
 
         /**
@@ -8207,7 +8633,7 @@
          *
          * @hide
          */
-
+        @Readable
         public static final String SHOW_ROTATION_SUGGESTIONS = "show_rotation_suggestions";
 
         /**
@@ -8234,6 +8660,7 @@
          * introduced to rotation suggestions.
          * @hide
          */
+        @Readable
         public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED =
                 "num_rotation_suggestions_accepted";
 
@@ -8246,6 +8673,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String ENABLED_NOTIFICATION_ASSISTANT =
                 "enabled_notification_assistant";
 
@@ -8259,6 +8687,7 @@
          */
         @Deprecated
         @UnsupportedAppUsage
+        @Readable
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
         /**
@@ -8270,6 +8699,7 @@
          */
         @Deprecated
         @TestApi
+        @Readable
         public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
                 "enabled_notification_policy_access_packages";
 
@@ -8283,6 +8713,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
 
@@ -8291,6 +8722,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
 
         /**
@@ -8298,6 +8730,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String PRINT_SERVICE_SEARCH_URI = "print_service_search_uri";
 
         /**
@@ -8305,6 +8738,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String PAYMENT_SERVICE_SEARCH_URI = "payment_service_search_uri";
 
         /**
@@ -8312,6 +8746,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOFILL_SERVICE_SEARCH_URI = "autofill_service_search_uri";
 
         /**
@@ -8320,6 +8755,7 @@
          * <p>
          * Type : int (0 to show hints, 1 to skip showing hints)
          */
+        @Readable
         public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
 
         /**
@@ -8327,6 +8763,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms";
 
         /**
@@ -8337,6 +8774,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
                 "lock_screen_show_notifications";
 
@@ -8347,6 +8785,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS =
                 "lock_screen_show_silent_notifications";
 
@@ -8357,6 +8796,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
 
         /**
@@ -8365,6 +8805,7 @@
          * {@link android.net.Uri#encode(String)} and separated by ':'.
          * @hide
          */
+        @Readable
         public static final String TV_INPUT_HIDDEN_INPUTS = "tv_input_hidden_inputs";
 
         /**
@@ -8373,6 +8814,7 @@
          * and separated by ','. Each pair is separated by ':'.
          * @hide
          */
+        @Readable
         public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels";
 
         /**
@@ -8390,6 +8832,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs";
 
         /**
@@ -8399,6 +8842,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String USB_AUDIO_AUTOMATIC_ROUTING_DISABLED =
                 "usb_audio_automatic_routing_disabled";
 
@@ -8414,6 +8858,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SLEEP_TIMEOUT = "sleep_timeout";
 
         /**
@@ -8428,12 +8873,14 @@
          *
          * @hide
          */
+        @Readable
         public static final String ATTENTIVE_TIMEOUT = "attentive_timeout";
 
         /**
          * Controls whether double tap to wake is enabled.
          * @hide
          */
+        @Readable
         public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
 
         /**
@@ -8447,6 +8894,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ASSISTANT = "assistant";
 
         /**
@@ -8454,6 +8902,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
 
         /**
@@ -8461,6 +8910,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
 
         /**
@@ -8468,6 +8918,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
                 "emergency_gesture_sound_enabled";
 
@@ -8477,6 +8928,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
                 "camera_double_tap_power_gesture_disabled";
 
@@ -8486,6 +8938,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
                 "camera_double_twist_to_flip_enabled";
 
@@ -8495,6 +8948,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAMERA_LIFT_TRIGGER_ENABLED = "camera_lift_trigger_enabled";
 
         /**
@@ -8510,6 +8964,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String FLASHLIGHT_AVAILABLE = "flashlight_available";
 
         /**
@@ -8517,18 +8972,21 @@
          *
          * @hide
          */
+        @Readable
         public static final String FLASHLIGHT_ENABLED = "flashlight_enabled";
 
         /**
          * Whether or not face unlock is allowed on Keyguard.
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_KEYGUARD_ENABLED = "face_unlock_keyguard_enabled";
 
         /**
          * Whether or not face unlock dismisses the keyguard.
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_DISMISSES_KEYGUARD =
                 "face_unlock_dismisses_keyguard";
 
@@ -8536,6 +8994,7 @@
          * Whether or not media is shown automatically when bypassing as a heads up.
          * @hide
          */
+        @Readable
         public static final String SHOW_MEDIA_WHEN_BYPASSING =
                 "show_media_when_bypassing";
 
@@ -8544,6 +9003,7 @@
          * truth is obtained through the HAL.
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_ATTENTION_REQUIRED =
                 "face_unlock_attention_required";
 
@@ -8552,6 +9012,7 @@
          * cached value, the source of truth is obtained through the HAL.
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_DIVERSITY_REQUIRED =
                 "face_unlock_diversity_required";
 
@@ -8560,6 +9021,7 @@
          * Whether or not face unlock is allowed for apps (through BiometricPrompt).
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_APP_ENABLED = "face_unlock_app_enabled";
 
         /**
@@ -8569,6 +9031,7 @@
          * setConfirmationRequired API.
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
                 "face_unlock_always_require_confirmation";
 
@@ -8583,12 +9046,14 @@
          *
          * @hide
          */
+        @Readable
         public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll";
 
         /**
          * Whether or not debugging is enabled.
          * @hide
          */
+        @Readable
         public static final String BIOMETRIC_DEBUG_ENABLED =
                 "biometric_debug_enabled";
 
@@ -8597,6 +9062,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
 
         /**
@@ -8604,6 +9070,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
 
         /**
@@ -8611,6 +9078,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
                 "assist_gesture_silence_alerts_enabled";
 
@@ -8619,6 +9087,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_GESTURE_WAKE_ENABLED =
                 "assist_gesture_wake_enabled";
 
@@ -8630,36 +9099,42 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
         /**
          * Control whether Trust Agents are in active unlock or extend unlock mode.
          * @hide
          */
+        @Readable
         public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock";
 
         /**
          * Control whether the screen locks when trust is lost.
          * @hide
          */
+        @Readable
         public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost";
 
         /**
          * Control whether Night display is currently activated.
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated";
 
         /**
          * Control whether Night display will automatically activate/deactivate.
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
                 "night_display_color_temperature";
 
@@ -8668,6 +9143,7 @@
          * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
                 "night_display_custom_start_time";
 
@@ -8676,6 +9152,7 @@
          * Represented as milliseconds from midnight (e.g. 21600000 == 6am).
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
 
         /**
@@ -8684,6 +9161,7 @@
          * legacy cases, this is represented by the time in milliseconds (since epoch).
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
                 "night_display_last_activated_time";
 
@@ -8691,6 +9169,7 @@
          * Control whether display white balance is currently enabled.
          * @hide
          */
+        @Readable
         public static final String DISPLAY_WHITE_BALANCE_ENABLED = "display_white_balance_enabled";
 
         /**
@@ -8701,6 +9180,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
 
         /**
@@ -8710,6 +9190,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String VR_DISPLAY_MODE = "vr_display_mode";
 
         /**
@@ -8743,6 +9224,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
 
         /**
@@ -8750,6 +9232,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MANAGED_PROFILE_CONTACT_REMOTE_SEARCH =
                 "managed_profile_contact_remote_search";
 
@@ -8758,6 +9241,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CROSS_PROFILE_CALENDAR_ENABLED =
                 "cross_profile_calendar_enabled";
 
@@ -8766,6 +9250,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED =
                 "automatic_storage_manager_enabled";
 
@@ -8774,6 +9259,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
                 "automatic_storage_manager_days_to_retain";
 
@@ -8789,6 +9275,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED =
                 "automatic_storage_manager_bytes_cleared";
 
@@ -8797,6 +9284,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN =
                 "automatic_storage_manager_last_run";
         /**
@@ -8806,6 +9294,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY =
                 "automatic_storage_manager_turned_off_by_policy";
 
@@ -8813,6 +9302,7 @@
          * Whether SystemUI navigation keys is enabled.
          * @hide
          */
+        @Readable
         public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
                 "system_navigation_keys_enabled";
 
@@ -8821,6 +9311,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String QS_TILES = "sysui_qs_tiles";
 
         /**
@@ -8831,6 +9322,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CONTROLS_ENABLED = "controls_enabled";
 
         /**
@@ -8840,6 +9332,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String POWER_MENU_LOCKED_SHOW_CONTENT =
                 "power_menu_locked_show_content";
 
@@ -8849,18 +9342,21 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled";
 
         /**
          * Has this pairable device been paired or upgraded from a previously paired system.
          * @hide
          */
+        @Readable
         public static final String DEVICE_PAIRED = "device_paired";
 
         /**
          * Specifies additional package name for broadcasting the CMAS messages.
          * @hide
          */
+        @Readable
         public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
 
         /**
@@ -8870,6 +9366,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String NOTIFICATION_BADGING = "notification_badging";
 
         /**
@@ -8879,6 +9376,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
 
         /**
@@ -8887,6 +9385,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String BUBBLE_IMPORTANT_CONVERSATIONS
                 = "bubble_important_conversations";
 
@@ -8896,18 +9395,21 @@
          *
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
 
         /**
          * Comma separated list of QS tiles that have been auto-added already.
          * @hide
          */
+        @Readable
         public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
 
         /**
          * Whether the Lockdown button should be shown in the power menu.
          * @hide
          */
+        @Readable
         public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
 
         /**
@@ -8935,6 +9437,7 @@
          * Type: string
          * @hide
          */
+        @Readable
         public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
 
 
@@ -8952,6 +9455,7 @@
          * Type: string
          * @hide
          */
+        @Readable
         public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS =
                 "backup_local_transport_parameters";
 
@@ -8960,6 +9464,7 @@
          * the user is driving.
          * @hide
          */
+        @Readable
         public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
 
         /**
@@ -8969,6 +9474,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
 
         /** @hide */
@@ -8985,6 +9491,7 @@
          * The number of times (integer) the user has manually enabled battery saver.
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MANUAL_ACTIVATION_COUNT =
                 "low_power_manual_activation_count";
 
@@ -8994,6 +9501,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_WARNING_ACKNOWLEDGED =
                 "low_power_warning_acknowledged";
 
@@ -9002,6 +9510,7 @@
          * suppressed.
          * @hide
          */
+        @Readable
         public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
                 "suppress_auto_battery_saver_suggestion";
 
@@ -9010,6 +9519,7 @@
          * Type: string
          * @hide
          */
+        @Readable
         public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
                 "packages_to_clear_data_before_full_restore";
 
@@ -9018,6 +9528,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
                 "location_access_check_interval_millis";
 
@@ -9026,6 +9537,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
                 "location_access_check_delay_millis";
 
@@ -9035,6 +9547,7 @@
          */
         @SystemApi
         @Deprecated
+        @Readable
         public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE =
                 "location_permissions_upgrade_to_q_mode";
 
@@ -9043,6 +9556,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
 
         /**
@@ -9053,6 +9567,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES =
                 "theme_customization_overlay_packages";
 
@@ -9063,6 +9578,7 @@
          *  2 = fully gestural
          * @hide
          */
+        @Readable
         public static final String NAVIGATION_MODE =
                 "navigation_mode";
 
@@ -9070,6 +9586,7 @@
          * Scale factor for the back gesture inset size on the left side of the screen.
          * @hide
          */
+        @Readable
         public static final String BACK_GESTURE_INSET_SCALE_LEFT =
                 "back_gesture_inset_scale_left";
 
@@ -9077,6 +9594,7 @@
          * Scale factor for the back gesture inset size on the right side of the screen.
          * @hide
          */
+        @Readable
         public static final String BACK_GESTURE_INSET_SCALE_RIGHT =
                 "back_gesture_inset_scale_right";
 
@@ -9086,30 +9604,35 @@
          * No VALIDATOR as this setting will not be backed up.
          * @hide
          */
+        @Readable
         public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
 
         /**
          * Controls whether aware is enabled.
          * @hide
          */
+        @Readable
         public static final String AWARE_ENABLED = "aware_enabled";
 
         /**
          * Controls whether aware_lock is enabled.
          * @hide
          */
+        @Readable
         public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
 
         /**
          * Controls whether tap gesture is enabled.
          * @hide
          */
+        @Readable
         public static final String TAP_GESTURE = "tap_gesture";
 
         /**
          * Controls whether the people strip is enabled.
          * @hide
          */
+        @Readable
         public static final String PEOPLE_STRIP = "people_strip";
 
         /**
@@ -9119,6 +9642,7 @@
          * @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS
          * @hide
          */
+        @Readable
         public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
 
         /**
@@ -9127,6 +9651,7 @@
          * @see Settings.Secure#MEDIA_CONTROLS_RESUME
          * @hide
          */
+        @Readable
         public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
 
         /**
@@ -9138,6 +9663,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
                 "accessibility_magnification_mode";
 
@@ -9173,6 +9699,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
                 "accessibility_magnification_capability";
 
@@ -9182,6 +9709,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
                 "accessibility_show_window_magnification_prompt";
 
@@ -9198,6 +9726,7 @@
          * @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_BUTTON_MODE =
                 "accessibility_button_mode";
 
@@ -9226,6 +9755,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
                 "accessibility_floating_menu_size";
 
@@ -9238,6 +9768,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
                 "accessibility_floating_menu_icon_type";
 
@@ -9246,6 +9777,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED =
                 "accessibility_floating_menu_fade_enabled";
 
@@ -9255,6 +9787,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
                 "accessibility_floating_menu_opacity";
 
@@ -9263,6 +9796,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
 
         /**
@@ -9274,6 +9808,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String[] LEGACY_RESTORE_SETTINGS = {
                 ENABLED_NOTIFICATION_LISTENERS,
                 ENABLED_NOTIFICATION_ASSISTANT,
@@ -9285,6 +9820,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
                 "reminder_exp_learning_time_elapsed";
 
@@ -9293,6 +9829,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
                 "reminder_exp_learning_event_count";
 
@@ -9406,6 +9943,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
 
         /**
@@ -9414,6 +9952,7 @@
          * Type: int
          * @hide
          */
+        @Readable
         public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
 
         /**
@@ -9421,6 +9960,7 @@
          * <p>1 = apply ramping ringer
          * <p>0 = do not apply ramping ringer
          */
+        @Readable
         public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
 
         /**
@@ -9432,12 +9972,14 @@
          * No longer used. Should be removed once all dependencies have been updated.
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED =
                 "enable_accessibility_global_gesture_enabled";
 
         /**
          * Whether Airplane Mode is on.
          */
+        @Readable
         public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
 
         /**
@@ -9445,30 +9987,36 @@
          * {@hide}
          */
         @SystemApi
+        @Readable
         public static final String THEATER_MODE_ON = "theater_mode_on";
 
         /**
          * Constant for use in AIRPLANE_MODE_RADIOS to specify Bluetooth radio.
          */
+        @Readable
         public static final String RADIO_BLUETOOTH = "bluetooth";
 
         /**
          * Constant for use in AIRPLANE_MODE_RADIOS to specify Wi-Fi radio.
          */
+        @Readable
         public static final String RADIO_WIFI = "wifi";
 
         /**
          * {@hide}
          */
+        @Readable
         public static final String RADIO_WIMAX = "wimax";
         /**
          * Constant for use in AIRPLANE_MODE_RADIOS to specify Cellular radio.
          */
+        @Readable
         public static final String RADIO_CELL = "cell";
 
         /**
          * Constant for use in AIRPLANE_MODE_RADIOS to specify NFC radio.
          */
+        @Readable
         public static final String RADIO_NFC = "nfc";
 
         /**
@@ -9476,6 +10024,7 @@
          * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are
          * included in the comma separated list.
          */
+        @Readable
         public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
 
         /**
@@ -9487,6 +10036,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
 
         /**
@@ -9494,6 +10044,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
 
         /**
@@ -9501,6 +10052,7 @@
          * See {@link android.bluetooth.BluetoothProfile}.
          * {@hide}
          */
+        @Readable
         public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
 
         /**
@@ -9513,6 +10065,7 @@
          *   "00:11:22,0;01:02:03:04,2"
          * @hide
          */
+        @Readable
        public static final String BLUETOOTH_INTEROPERABILITY_LIST = "bluetooth_interoperability_list";
 
         /**
@@ -9525,6 +10078,7 @@
          * @deprecated This is no longer used or set by the platform.
          */
         @Deprecated
+        @Readable
         public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
 
         /**
@@ -9556,60 +10110,70 @@
          * Value to specify if the user prefers the date, time and time zone
          * to be automatically fetched from the network (NITZ). 1=yes, 0=no
          */
+        @Readable
         public static final String AUTO_TIME = "auto_time";
 
         /**
          * Value to specify if the user prefers the time zone
          * to be automatically fetched from the network (NITZ). 1=yes, 0=no
          */
+        @Readable
         public static final String AUTO_TIME_ZONE = "auto_time_zone";
 
         /**
          * URI for the car dock "in" event sound.
          * @hide
          */
+        @Readable
         public static final String CAR_DOCK_SOUND = "car_dock_sound";
 
         /**
          * URI for the car dock "out" event sound.
          * @hide
          */
+        @Readable
         public static final String CAR_UNDOCK_SOUND = "car_undock_sound";
 
         /**
          * URI for the desk dock "in" event sound.
          * @hide
          */
+        @Readable
         public static final String DESK_DOCK_SOUND = "desk_dock_sound";
 
         /**
          * URI for the desk dock "out" event sound.
          * @hide
          */
+        @Readable
         public static final String DESK_UNDOCK_SOUND = "desk_undock_sound";
 
         /**
          * Whether to play a sound for dock events.
          * @hide
          */
+        @Readable
         public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled";
 
         /**
          * Whether to play a sound for dock events, only when an accessibility service is on.
          * @hide
          */
+        @Readable
         public static final String DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY = "dock_sounds_enabled_when_accessbility";
 
         /**
          * URI for the "device locked" (keyguard shown) sound.
          * @hide
          */
+        @Readable
         public static final String LOCK_SOUND = "lock_sound";
 
         /**
          * URI for the "device unlocked" sound.
          * @hide
          */
+        @Readable
         public static final String UNLOCK_SOUND = "unlock_sound";
 
         /**
@@ -9617,24 +10181,28 @@
          * state without unlocking.
          * @hide
          */
+        @Readable
         public static final String TRUSTED_SOUND = "trusted_sound";
 
         /**
          * URI for the low battery sound file.
          * @hide
          */
+        @Readable
         public static final String LOW_BATTERY_SOUND = "low_battery_sound";
 
         /**
          * Whether to play a sound for low-battery alerts.
          * @hide
          */
+        @Readable
         public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
 
         /**
          * URI for the "wireless charging started" sound.
          * @hide
          */
+        @Readable
         public static final String WIRELESS_CHARGING_STARTED_SOUND =
                 "wireless_charging_started_sound";
 
@@ -9642,6 +10210,7 @@
          * URI for "wired charging started" sound.
          * @hide
          */
+        @Readable
         public static final String CHARGING_STARTED_SOUND = "charging_started_sound";
 
         /**
@@ -9671,6 +10240,7 @@
          * </ul>
          * These values can be OR-ed together.
          */
+        @Readable
         public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
 
         /**
@@ -9678,6 +10248,7 @@
          * in the power menu.
          * @hide
          */
+        @Readable
         public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
 
         /**
@@ -9686,6 +10257,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app";
 
         /**
@@ -9694,29 +10266,34 @@
          *
          * @hide
          */
+        @Readable
         public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user";
 
         /**
          * Whether ADB over USB is enabled.
          */
+        @Readable
         public static final String ADB_ENABLED = "adb_enabled";
 
         /**
          * Whether ADB over Wifi is enabled.
          * @hide
          */
+        @Readable
         public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled";
 
         /**
          * Whether Views are allowed to save their attribute data.
          * @hide
          */
+        @Readable
         public static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes";
 
         /**
          * Which application package is allowed to save View attribute data.
          * @hide
          */
+        @Readable
         public static final String DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE =
                 "debug_view_attributes_application_package";
 
@@ -9724,12 +10301,14 @@
          * Whether assisted GPS should be enabled or not.
          * @hide
          */
+        @Readable
         public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
 
         /**
          * Whether bluetooth is enabled/disabled
          * 0=disabled. 1=enabled.
          */
+        @Readable
         public static final String BLUETOOTH_ON = "bluetooth_on";
 
         /**
@@ -9738,6 +10317,7 @@
          *                            1 = CDMA Cell Broadcast SMS enabled
          * @hide
          */
+        @Readable
         public static final String CDMA_CELL_BROADCAST_SMS =
                 "cdma_cell_broadcast_sms";
 
@@ -9747,6 +10327,7 @@
          *                       2 = Roaming on any networks
          * @hide
          */
+        @Readable
         public static final String CDMA_ROAMING_MODE = "roaming_settings";
 
         /**
@@ -9754,6 +10335,7 @@
          *                                1 = NV
          * @hide
          */
+        @Readable
         public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
 
         /**
@@ -9763,44 +10345,49 @@
          *
          * @hide
          */
+        @Readable
         public static final String DEFAULT_RESTRICT_BACKGROUND_DATA =
                 "default_restrict_background_data";
 
         /** Inactivity timeout to track mobile data activity.
-        *
-        * If set to a positive integer, it indicates the inactivity timeout value in seconds to
-        * infer the data activity of mobile network. After a period of no activity on mobile
-        * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
-        * intent is fired to indicate a transition of network status from "active" to "idle". Any
-        * subsequent activity on mobile networks triggers the firing of {@code
-        * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
-        *
-        * 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";
+         *
+         * If set to a positive integer, it indicates the inactivity timeout value in seconds to
+         * infer the data activity of mobile network. After a period of no activity on mobile
+         * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
+         * intent is fired to indicate a transition of network status from "active" to "idle". Any
+         * subsequent activity on mobile networks triggers the firing of {@code
+         * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
+         *
+         * Network activity refers to transmitting or receiving data on the network interfaces.
+         *
+         * Tracking is disabled if set to zero or negative value.
+         *
+         * @hide
+         */
+        @Readable
+        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";
+        /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
+         * but for Wifi network.
+         * @hide
+         */
+        @Readable
+        public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
 
-       /**
-        * Whether or not data roaming is enabled. (0 = false, 1 = true)
-        */
-       public static final String DATA_ROAMING = "data_roaming";
+        /**
+         * Whether or not data roaming is enabled. (0 = false, 1 = true)
+         */
+        @Readable
+        public static final String DATA_ROAMING = "data_roaming";
 
-       /**
-        * The value passed to a Mobile DataConnection via bringUp which defines the
-        * number of retries to preform when setting up the initial connection. The default
-        * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
-        * @hide
-        */
-       public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
+        /**
+         * The value passed to a Mobile DataConnection via bringUp which defines the
+         * number of retries to perform when setting up the initial connection. The default
+         * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
+         * @hide
+         */
+        @Readable
+        public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
 
         /**
          * Whether any package can be on external storage. When this is true, any
@@ -9808,6 +10395,7 @@
          * or moving onto external storage. (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external";
 
         /**
@@ -9822,6 +10410,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
 
         /**
@@ -9833,6 +10422,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String EUICC_PROVISIONED = "euicc_provisioned";
 
         /**
@@ -9848,6 +10438,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
 
         /**
@@ -9863,6 +10454,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
 
         /**
@@ -9871,6 +10463,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
                 = "force_resizable_activities";
 
@@ -9878,6 +10471,7 @@
          * Whether to enable experimental freeform support for windows.
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT
                 = "enable_freeform_support";
 
@@ -9885,6 +10479,7 @@
          * Whether to enable experimental desktop mode on secondary displays.
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS =
                 "force_desktop_mode_on_external_displays";
 
@@ -9896,6 +10491,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
                 "enable_sizecompat_freeform";
 
@@ -9906,6 +10502,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW =
                 "enable_non_resizable_multi_window";
@@ -9917,6 +10514,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR =
                 "render_shadows_in_compositor";
 
@@ -9925,6 +10523,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_USE_BLAST_ADAPTER_VR =
                 "use_blast_adapter_vr";
 
@@ -9933,6 +10532,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_USE_BLAST_ADAPTER_SV =
                 "use_blast_adapter_sv";
 
@@ -9942,21 +10542,24 @@
          *
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH =
                 "wm_display_settings_path";
 
-       /**
+        /**
         * Whether user has enabled development settings.
         */
-       public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+        @Readable
+        public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
 
-       /**
+        /**
         * Whether the device has been provisioned (0 = false, 1 = true).
         * <p>On a multiuser device with a separate system user, the screen may be locked
         * as soon as this is set to true and further activities cannot be launched on the
         * system user unless they are marked to show over keyguard.
         */
-       public static final String DEVICE_PROVISIONED = "device_provisioned";
+        @Readable
+        public static final String DEVICE_PROVISIONED = "device_provisioned";
 
         /**
          * Indicates whether mobile data should be allowed while the device is being provisioned.
@@ -9969,52 +10572,58 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
                 "device_provisioning_mobile_data";
 
-       /**
+        /**
         * The saved value for WindowManagerService.setForcedDisplaySize().
         * Two integers separated by a comma.  If unset, then use the real display size.
         * @hide
         */
-       public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
+        @Readable
+        public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
 
-       /**
+        /**
         * The saved value for WindowManagerService.setForcedDisplayScalingMode().
         * 0 or unset if scaling is automatic, 1 if scaling is disabled.
         * @hide
         */
-       public static final String DISPLAY_SCALING_FORCE = "display_scaling_force";
+        @Readable
+        public static final String DISPLAY_SCALING_FORCE = "display_scaling_force";
 
-       /**
+        /**
         * The maximum size, in bytes, of a download that the download manager will transfer over
         * a non-wifi connection.
         * @hide
         */
-       public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
+        @Readable
+        public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
                "download_manager_max_bytes_over_mobile";
 
-       /**
+        /**
         * The recommended maximum size, in bytes, of a download that the download manager should
         * transfer over a non-wifi connection. Over this size, the use will be warned, but will
         * have the option to start the download over the mobile connection anyway.
         * @hide
         */
-       public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
+        @Readable
+        public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
                "download_manager_recommended_max_bytes_over_mobile";
 
-       /**
+        /**
         * @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead
         */
-       @Deprecated
-       public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
+        @Deprecated
+        public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
 
-       /**
+        /**
         * Whether HDMI control shall be enabled. If disabled, no CEC/MHL command will be
         * sent or processed. (0 = false, 1 = true)
         * @hide
         */
-       public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
+        @Readable
+        public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
 
         /**
          * Controls whether volume control commands via HDMI CEC are enabled. (0 = false, 1 =
@@ -10050,16 +10659,18 @@
          * @hide
          * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)
          */
+        @Readable
         public static final String HDMI_CONTROL_VOLUME_CONTROL_ENABLED =
                 "hdmi_control_volume_control_enabled";
 
-       /**
+        /**
         * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
         * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
         * be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be
         * activated.
         * @hide
         */
+        @Readable
         public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED =
                 "hdmi_system_audio_control_enabled";
 
@@ -10069,6 +10680,7 @@
          * disabled, you can only switch the input via controls on this device.
          * @hide
          */
+        @Readable
         public static final String HDMI_CEC_SWITCH_ENABLED =
                 "hdmi_cec_switch_enabled";
 
@@ -10076,6 +10688,7 @@
          * HDMI CEC version to use. Defaults to v1.4b.
          * @hide
          */
+        @Readable
         public static final String HDMI_CEC_VERSION =
                 "hdmi_cec_version";
 
@@ -10085,6 +10698,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED =
                 "hdmi_control_auto_wakeup_enabled";
 
@@ -10094,6 +10708,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED =
                 "hdmi_control_auto_device_off_enabled";
 
@@ -10115,6 +10730,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP =
                 "hdmi_control_send_standby_on_sleep";
 
@@ -10122,6 +10738,7 @@
          * Whether or not media is shown automatically when bypassing as a heads up.
          * @hide
          */
+        @Readable
         public static final String SHOW_MEDIA_ON_QUICK_SETTINGS =
                 "qs_media_player";
 
@@ -10131,6 +10748,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
                 "location_background_throttle_interval_ms";
 
@@ -10139,6 +10757,7 @@
          * to request.
          * @hide
          */
+        @Readable
         public static final String LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
                 "location_background_throttle_proximity_alert_interval_ms";
 
@@ -10146,6 +10765,7 @@
          * Packages that are whitelisted for background throttling (throttling will not be applied).
          * @hide
          */
+        @Readable
         public static final String LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST =
             "location_background_throttle_package_whitelist";
 
@@ -10155,6 +10775,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
                 "location_ignore_settings_package_whitelist";
 
@@ -10163,23 +10784,26 @@
         * (0 = false, 1 = true)
         * @hide
         */
-       public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled";
+        @Readable
+        public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled";
 
-       /**
+        /**
         * Whether TV will charge the mobile device connected at MHL port. (0 = false, 1 = true)
         * @hide
         */
-       public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled";
+        @Readable
+        public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled";
 
-       /**
+        /**
         * Whether mobile data connections are allowed by the user.  See
         * ConnectivityManager for more info.
         * @hide
         */
-       @UnsupportedAppUsage
-       public static final String MOBILE_DATA = "mobile_data";
+        @UnsupportedAppUsage
+        @Readable
+        public static final String MOBILE_DATA = "mobile_data";
 
-       /**
+        /**
         * Whether the mobile data connection should remain active even when higher
         * priority networks like WiFi are active, to help make network switching faster.
         *
@@ -10188,7 +10812,8 @@
         * (0 = disabled, 1 = enabled)
         * @hide
         */
-       public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+        @Readable
+        public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
 
         /**
          * Whether the wifi data connection should remain active even when higher
@@ -10201,195 +10826,249 @@
          * (0 = disabled, 1 = enabled)
          * @hide
          */
+        @Readable
         public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
 
         /**
          * Size of the event buffer for IP connectivity metrics.
          * @hide
          */
+        @Readable
         public static final String CONNECTIVITY_METRICS_BUFFER_SIZE =
               "connectivity_metrics_buffer_size";
 
-       /** {@hide} */
-       public static final String NETSTATS_ENABLED = "netstats_enabled";
-       /** {@hide} */
-       public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
-       /** {@hide} */
-       @Deprecated
-       public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
-       /** {@hide} */
-       public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
-       /** {@hide} */
-       public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
-       /** {@hide} */
-       public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled";
-       /** {@hide} */
-       public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = "netstats_combine_subtype_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_ENABLED = "netstats_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+        /**
+         * @deprecated
+         * {@hide}
+         */
+        @Deprecated
+        @Readable
+        public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
+                "netstats_combine_subtype_enabled";
 
-       /** {@hide} */
-       public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
-       /** {@hide} */
-       public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
-       /** {@hide} */
-       public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
-       /** {@hide} */
-       public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
 
-       /** {@hide} */
-       public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
-       /** {@hide} */
-       public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
-       /** {@hide} */
-       public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
-       /** {@hide} */
-       public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
 
-       /** {@hide} */
-       public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration";
-       /** {@hide} */
-       public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes";
-       /** {@hide} */
-       public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
-       /** {@hide} */
-       public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_TAG_BUCKET_DURATION =
+                "netstats_uid_tag_bucket_duration";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_TAG_PERSIST_BYTES =
+                "netstats_uid_tag_persist_bytes";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
+        /** {@hide} */
+        @Readable
+        public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
 
-       /** {@hide} */
-       public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
-       /** {@hide} */
-       public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
-       /** {@hide} */
-       public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
-       /** {@hide} */
-       public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
-       /** {@hide} */
-       public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH =
+                "netpolicy_quota_frac_multipath";
 
-       /** {@hide} */
-       public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+        /** {@hide} */
+        @Readable
+        public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
 
-       /**
+        /**
         * User preference for which network(s) should be used. Only the
         * connectivity service should touch this.
         */
-       public static final String NETWORK_PREFERENCE = "network_preference";
+        @Readable
+        public static final String NETWORK_PREFERENCE = "network_preference";
 
-       /**
+        /**
         * Which package name to use for network scoring. If null, or if the package is not a valid
         * scorer app, external network scores will neither be requested nor accepted.
         * @hide
         */
-       @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-       public static final String NETWORK_SCORER_APP = "network_scorer_app";
+        @Readable
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        public static final String NETWORK_SCORER_APP = "network_scorer_app";
 
         /**
          * Whether night display forced auto mode is available.
          * 0 = unavailable, 1 = available.
          * @hide
          */
+        @Readable
         public static final String NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE =
                 "night_display_forced_auto_mode_available";
 
-       /**
+        /**
         * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
         * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
         * exceeded.
         * @hide
         */
-       public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
+        @Readable
+        public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
 
-       /**
+        /**
         * The length of time in milli-seconds that automatic small adjustments to
         * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
         * @hide
         */
-       public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
+        @Readable
+        public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
 
-       /** Preferred NTP server. {@hide} */
-       public static final String NTP_SERVER = "ntp_server";
-       /** Timeout in milliseconds to wait for NTP server. {@hide} */
-       public static final String NTP_TIMEOUT = "ntp_timeout";
+        /** Preferred NTP server. {@hide} */
+        @Readable
+        public static final String NTP_SERVER = "ntp_server";
+        /** Timeout in milliseconds to wait for NTP server. {@hide} */
+        @Readable
+        public static final String NTP_TIMEOUT = "ntp_timeout";
 
-       /** {@hide} */
-       public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
+        /** {@hide} */
+        @Readable
+        public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
 
         /**
          * Whether or not Settings should enable psd API.
          * {@hide}
          */
+        @Readable
         public static final String SETTINGS_USE_PSD_API = "settings_use_psd_api";
 
         /**
          * Whether or not Settings should enable external provider API.
          * {@hide}
          */
+        @Readable
         public static final String SETTINGS_USE_EXTERNAL_PROVIDER_API =
                 "settings_use_external_provider_api";
 
-       /**
+        /**
         * Sample validity in seconds to configure for the system DNS resolver.
         * {@hide}
         */
-       public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
+        @Readable
+        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 =
+        @Readable
+        public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
                 "dns_resolver_success_threshold_percent";
 
-       /**
+        /**
         * 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";
+        @Readable
+        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";
+        @Readable
+        public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
 
-       /**
+        /**
         * Whether to disable the automatic scheduling of system updates.
         * 1 = system updates won't be automatically scheduled (will always
         * present notification instead).
         * 0 = system updates will be automatically scheduled. (default)
         * @hide
         */
-       @SystemApi
-       public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
+        @SystemApi
+        @Readable
+        public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
 
-       /** Timeout for package verification.
+        /** Timeout for package verification.
         * @hide */
-       public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+        @Readable
+        public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
 
         /** Timeout for app integrity verification.
          * @hide */
+        @Readable
         public static final String APP_INTEGRITY_VERIFICATION_TIMEOUT =
                 "app_integrity_verification_timeout";
 
-       /** Default response code for package verification.
+        /** Default response code for package verification.
         * @hide */
-       public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
+        @Readable
+        public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
 
-       /**
+        /**
         * Show package verification setting in the Settings app.
         * 1 = show (default)
         * 0 = hide
         * @hide
         */
-       public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
+        @Readable
+        public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
 
-       /**
+        /**
         * Run package verification on apps installed through ADB/ADT/USB
         * 1 = perform package verification on ADB installs (default)
         * 0 = bypass package verification on ADB installs
         * @hide
         */
-       public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs";
+        @Readable
+        public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs";
 
         /**
          * Run integrity checks for integrity rule providers.
@@ -10397,117 +11076,131 @@
          * 1 = perform integrity verification on installs from rule providers
          * @hide
          */
+        @Readable
         public static final String INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER =
                 "verify_integrity_for_rule_provider";
 
-       /**
+        /**
         * Time since last fstrim (milliseconds) after which we force one to happen
         * during device startup.  If unset, the default is 3 days.
         * @hide
         */
-       public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval";
+        @Readable
+        public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval";
 
-       /**
+        /**
         * The interval in milliseconds at which to check packet counts on the
         * mobile data interface when screen is on, to detect possible data
         * connection problems.
         * @hide
         */
-       public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
+        @Readable
+        public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
                "pdp_watchdog_poll_interval_ms";
 
-       /**
+        /**
         * The interval in milliseconds at which to check packet counts on the
         * mobile data interface when screen is off, to detect possible data
         * connection problems.
         * @hide
         */
-       public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
+        @Readable
+        public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
                "pdp_watchdog_long_poll_interval_ms";
 
-       /**
+        /**
         * The interval in milliseconds at which to check packet counts on the
         * mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT}
         * outgoing packets has been reached without incoming packets.
         * @hide
         */
-       public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
+        @Readable
+        public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
                "pdp_watchdog_error_poll_interval_ms";
 
-       /**
+        /**
         * The number of outgoing packets sent without seeing an incoming packet
         * that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT}
         * device is logged to the event log
         * @hide
         */
-       public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
+        @Readable
+        public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
                "pdp_watchdog_trigger_packet_count";
 
-       /**
+        /**
         * The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS})
         * after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before
         * attempting data connection recovery.
         * @hide
         */
-       public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
+        @Readable
+        public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
                "pdp_watchdog_error_poll_count";
 
-       /**
+        /**
         * The number of failed PDP reset attempts before moving to something more
         * drastic: re-registering to the network.
         * @hide
         */
-       public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
+        @Readable
+        public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
                "pdp_watchdog_max_pdp_reset_fail_count";
 
-       /**
+        /**
         * URL to open browser on to allow user to manage a prepay account
         * @hide
         */
-       public static final String SETUP_PREPAID_DATA_SERVICE_URL =
+        @Readable
+        public static final String SETUP_PREPAID_DATA_SERVICE_URL =
                "setup_prepaid_data_service_url";
 
-       /**
+        /**
         * URL to attempt a GET on to see if this is a prepay device
         * @hide
         */
-       public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
+        @Readable
+        public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
                "setup_prepaid_detection_target_url";
 
-       /**
+        /**
         * Host to check for a redirect to after an attempt to GET
         * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
         * this is a prepaid device with zero balance.)
         * @hide
         */
-       public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
+        @Readable
+        public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
                "setup_prepaid_detection_redir_host";
 
-       /**
+        /**
         * The interval in milliseconds at which to check the number of SMS sent out without asking
         * for use permit, to limit the un-authorized SMS usage.
         *
         * @hide
         */
-       public static final String SMS_OUTGOING_CHECK_INTERVAL_MS =
+        @Readable
+        public static final String SMS_OUTGOING_CHECK_INTERVAL_MS =
                "sms_outgoing_check_interval_ms";
 
-       /**
+        /**
         * The number of outgoing SMS sent without asking for user permit (of {@link
         * #SMS_OUTGOING_CHECK_INTERVAL_MS}
         *
         * @hide
         */
-       public static final String SMS_OUTGOING_CHECK_MAX_COUNT =
+        @Readable
+        public static final String SMS_OUTGOING_CHECK_MAX_COUNT =
                "sms_outgoing_check_max_count";
 
-       /**
+        /**
         * Used to disable SMS short code confirmation - defaults to true.
         * True indcates we will do the check, etc.  Set to false to disable.
         * @see com.android.internal.telephony.SmsUsageMonitor
         * @hide
         */
-       public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation";
+        @Readable
+        public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation";
 
         /**
          * Used to select which country we use to determine premium sms codes.
@@ -10516,6 +11209,7 @@
          * or com.android.internal.telephony.SMSDispatcher.PREMIUM_RULE_USE_BOTH.
          * @hide
          */
+        @Readable
         public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule";
 
         /**
@@ -10523,6 +11217,7 @@
          * build config value.
          * @hide
          */
+        @Readable
         public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd";
 
         /**
@@ -10530,6 +11225,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String TETHER_SUPPORTED = "tether_supported";
 
         /**
@@ -10537,6 +11233,7 @@
          * which defaults to false.
          * @hide
          */
+        @Readable
         public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
 
         /**
@@ -10548,6 +11245,7 @@
          * note that empty fields can be omitted: "name,apn,,,,,,,,,310,260,,DUN"
          * @hide
          */
+        @Readable
         public static final String TETHER_DUN_APN = "tether_dun_apn";
 
         /**
@@ -10558,6 +11256,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
 
         /**
@@ -10567,6 +11266,7 @@
          * is interpreted as |false|.
          * @hide
          */
+        @Readable
         public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
                 "tether_enable_legacy_dhcp_server";
 
@@ -10581,6 +11281,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
 
         /**
@@ -10591,62 +11292,71 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String CARRIER_APP_NAMES = "carrier_app_names";
 
-       /**
+        /**
         * USB Mass Storage Enabled
         */
-       public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+        @Readable
+        public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
 
-       /**
+        /**
         * If this setting is set (to anything), then all references
         * to Gmail on the device must change to Google Mail.
         */
-       public static final String USE_GOOGLE_MAIL = "use_google_mail";
+        @Readable
+        public static final String USE_GOOGLE_MAIL = "use_google_mail";
 
         /**
          * Whether or not switching/creating users is enabled by user.
          * @hide
          */
+        @Readable
         public static final String USER_SWITCHER_ENABLED = "user_switcher_enabled";
 
         /**
          * Webview Data reduction proxy key.
          * @hide
          */
+        @Readable
         public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
                 "webview_data_reduction_proxy_key";
 
-       /**
+        /**
         * Name of the package used as WebView provider (if unset the provider is instead determined
         * by the system).
         * @hide
         */
-       @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-       public static final String WEBVIEW_PROVIDER = "webview_provider";
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
+        public static final String WEBVIEW_PROVIDER = "webview_provider";
 
-       /**
+        /**
         * Developer setting to enable WebView multiprocess rendering.
         * @hide
         */
-       @SystemApi
-       public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
+        @SystemApi
+        @Readable
+        public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
 
-       /**
+        /**
         * The maximum number of notifications shown in 24 hours when switching networks.
         * @hide
         */
-       public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
+        @Readable
+        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 =
+        @Readable
+        public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
               "network_switch_notification_rate_limit_millis";
 
-       /**
+        /**
         * Whether to automatically switch away from wifi networks that lose Internet access.
         * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
         * avoids such networks. Valid values are:
@@ -10657,16 +11367,18 @@
         *
         * @hide
         */
-       public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+        @Readable
+        public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
 
-       /**
+        /**
         * 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 =
+        @Readable
+        public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
                "network_metered_multipath_preference";
 
         /**
@@ -10675,6 +11387,7 @@
          * from data plan or data limit/warning set by the user.
          * @hide
          */
+        @Readable
         public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES =
                 "network_default_daily_multipath_quota_bytes";
 
@@ -10682,10 +11395,11 @@
          * Network watchlist last report time.
          * @hide
          */
+        @Readable
         public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME =
                 "network_watchlist_last_report_time";
 
-       /**
+        /**
         * The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of
         * colon-delimited key-value pairs. The key is the badging enum value defined in
         * android.net.ScoredNetwork and the value is the minimum sustained network throughput in
@@ -10693,25 +11407,28 @@
         *
         * @hide
         */
-       @SystemApi
-       public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
+        @SystemApi
+        @Readable
+        public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
 
-       /**
+        /**
         * Whether Wifi display is enabled/disabled
         * 0=disabled. 1=enabled.
         * @hide
         */
-       public static final String WIFI_DISPLAY_ON = "wifi_display_on";
+        @Readable
+        public static final String WIFI_DISPLAY_ON = "wifi_display_on";
 
-       /**
+        /**
         * Whether Wifi display certification mode is enabled/disabled
         * 0=disabled. 1=enabled.
         * @hide
         */
-       public static final String WIFI_DISPLAY_CERTIFICATION_ON =
+        @Readable
+        public static final String WIFI_DISPLAY_CERTIFICATION_ON =
                "wifi_display_certification_on";
 
-       /**
+        /**
         * WPS Configuration method used by Wifi display, this setting only
         * takes effect when WIFI_DISPLAY_CERTIFICATION_ON is 1 (enabled).
         *
@@ -10723,10 +11440,11 @@
         * WpsInfo.DISPLAY: use Display
         * @hide
         */
-       public static final String WIFI_DISPLAY_WPS_CONFIG =
+        @Readable
+        public static final String WIFI_DISPLAY_WPS_CONFIG =
            "wifi_display_wps_config";
 
-       /**
+        /**
         * Whether to notify the user of open networks.
         * <p>
         * If not connected and the scan results have an open network, we will
@@ -10738,68 +11456,78 @@
         * @deprecated This feature is no longer controlled by this setting in
         * {@link android.os.Build.VERSION_CODES#O}.
         */
-       @Deprecated
-       public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+        @Deprecated
+        @Readable
+        public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
                "wifi_networks_available_notification_on";
 
-       /**
+        /**
         * {@hide}
         */
-       public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+        @Readable
+        public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
                "wimax_networks_available_notification_on";
 
-       /**
+        /**
         * Delay (in seconds) before repeating the Wi-Fi networks available notification.
         * Connecting to a network will reset the timer.
         * @deprecated This is no longer used or set by the platform.
         */
-       @Deprecated
-       public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
+        @Deprecated
+        @Readable
+        public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
                "wifi_networks_available_repeat_delay";
 
-       /**
+        /**
         * 802.11 country code in ISO 3166 format
         * @hide
         */
-       public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
+        @Readable
+        public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
 
-       /**
+        /**
         * The interval in milliseconds to issue wake up scans when wifi needs
         * to connect. This is necessary to connect to an access point when
         * device is on the move and the screen is off.
         * @hide
         */
-       public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
+        @Readable
+        public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
                "wifi_framework_scan_interval_ms";
 
-       /**
+        /**
         * The interval in milliseconds after which Wi-Fi is considered idle.
         * When idle, it is possible for the device to be switched from Wi-Fi to
         * the mobile data network.
         * @hide
         */
-       public static final String WIFI_IDLE_MS = "wifi_idle_ms";
+        @Readable
+        public static final String WIFI_IDLE_MS = "wifi_idle_ms";
 
-       /**
+        /**
         * When the number of open networks exceeds this number, the
         * least-recently-used excess networks will be removed.
         * @deprecated This is no longer used or set by the platform.
         */
-       @Deprecated
-       public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+        @Deprecated
+        @Readable
+        public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
 
-       /**
+        /**
         * Whether the Wi-Fi should be on.  Only the Wi-Fi service should touch this.
         */
-       public static final String WIFI_ON = "wifi_on";
+        @Readable
+        public static final String WIFI_ON = "wifi_on";
 
-       /**
+        /**
         * Setting to allow scans to be enabled even wifi is turned off for connectivity.
         * @hide
         * @deprecated To be removed. Use {@link WifiManager#setScanAlwaysAvailable(boolean)} for
         * setting the value and {@link WifiManager#isScanAlwaysAvailable()} for query.
         */
-       public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
+        @Deprecated
+        @Readable
+        public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
                 "wifi_scan_always_enabled";
 
         /**
@@ -10809,6 +11537,8 @@
          * @hide
          * @deprecated To be removed.
          */
+        @Deprecated
+        @Readable
         public static final String WIFI_P2P_PENDING_FACTORY_RESET =
                 "wifi_p2p_pending_factory_reset";
 
@@ -10821,6 +11551,8 @@
          * setAutoShutdownEnabled(boolean)} for setting the value and {@link SoftApConfiguration#
          * isAutoShutdownEnabled()} for query.
          */
+        @Deprecated
+        @Readable
         public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
 
         /**
@@ -10833,6 +11565,7 @@
          */
         @Deprecated
         @SystemApi
+        @Readable
         public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
 
         /**
@@ -10842,6 +11575,7 @@
          * Type: int (0 for false, 1 for true)
          * @hide
          */
+        @Readable
         public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed";
 
         /**
@@ -10850,6 +11584,7 @@
          * Type: int (0 for false, 1 for true)
          * @hide
          */
+        @Readable
         public static final String NETWORK_SCORING_UI_ENABLED = "network_scoring_ui_enabled";
 
         /**
@@ -10859,6 +11594,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS =
                 "speed_label_cache_eviction_age_millis";
 
@@ -10877,6 +11613,8 @@
          * @hide
          * @deprecated To be removed.
          */
+        @Deprecated
+        @Readable
         public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                 "network_recommendations_enabled";
 
@@ -10890,6 +11628,7 @@
          * Type: string - package name
          * @hide
          */
+        @Readable
         public static final String NETWORK_RECOMMENDATIONS_PACKAGE =
                 "network_recommendations_package";
 
@@ -10901,6 +11640,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
 
         /**
@@ -10910,6 +11650,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
                 "recommended_network_evaluator_cache_expiry_ms";
 
@@ -10921,6 +11662,8 @@
          * @deprecated Use {@link WifiManager#setScanThrottleEnabled(boolean)} for setting the value
          * and {@link WifiManager#isScanThrottleEnabled()} for query.
          */
+        @Deprecated
+        @Readable
         public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
 
         /**
@@ -10928,24 +11671,28 @@
         * connectivity.
         * @hide
         */
+        @Readable
         public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
 
         /**
          * The length in milliseconds of a BLE scan window in a low-power scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
 
         /**
          * The length in milliseconds of a BLE scan window in a balanced scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
 
         /**
          * The length in milliseconds of a BLE scan window in a low-latency scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
                 "ble_scan_low_latency_window_ms";
 
@@ -10953,6 +11700,7 @@
          * The length in milliseconds of a BLE scan interval in a low-power scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
                 "ble_scan_low_power_interval_ms";
 
@@ -10960,6 +11708,7 @@
          * The length in milliseconds of a BLE scan interval in a balanced scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
                 "ble_scan_balanced_interval_ms";
 
@@ -10967,6 +11716,7 @@
          * The length in milliseconds of a BLE scan interval in a low-latency scan mode.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
@@ -10974,26 +11724,30 @@
          * The mode that BLE scanning clients will be moved to when in the background.
          * @hide
          */
+        @Readable
         public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
 
-       /**
+        /**
         * The interval in milliseconds to scan as used by the wifi supplicant
         * @hide
         */
-       public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
+        @Readable
+        public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
                "wifi_supplicant_scan_interval_ms";
 
         /**
          * whether frameworks handles wifi auto-join
          * @hide
          */
-       public static final String WIFI_ENHANCED_AUTO_JOIN =
+        @Readable
+        public static final String WIFI_ENHANCED_AUTO_JOIN =
                 "wifi_enhanced_auto_join";
 
         /**
          * whether settings show RSSI
          * @hide
          */
+        @Readable
         public static final String WIFI_NETWORK_SHOW_RSSI =
                 "wifi_network_show_rssi";
 
@@ -11001,31 +11755,36 @@
         * The interval in milliseconds to scan at supplicant when p2p is connected
         * @hide
         */
-       public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS =
+        @Readable
+        public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS =
                "wifi_scan_interval_p2p_connected_ms";
 
-       /**
+        /**
         * Whether the Wi-Fi watchdog is enabled.
         */
-       public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
+        @Readable
+        public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
 
-       /**
+        /**
         * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
         * the setting needs to be set to 0 to disable it.
         * @hide
         */
-       @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-       public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
+        public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
                "wifi_watchdog_poor_network_test_enabled";
 
-       /**
+        /**
         * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
         * will enable it. In the future, additional values may be supported.
         * @hide
         * @deprecated Use {@link WifiManager#setVerboseLoggingEnabled(boolean)} for setting the
         * value and {@link WifiManager#isVerboseLoggingEnabled()} for query.
         */
-       public static final String WIFI_VERBOSE_LOGGING_ENABLED =
+        @Deprecated
+        @Readable
+        public static final String WIFI_VERBOSE_LOGGING_ENABLED =
                "wifi_verbose_logging_enabled";
 
         /**
@@ -11035,6 +11794,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
                 "wifi_connected_mac_randomization_enabled";
 
@@ -11051,24 +11811,28 @@
          * @hide
          * @deprecated This is no longer used or set by the platform.
          */
+        @Deprecated
+        @Readable
         public static final String WIFI_SCORE_PARAMS =
                 "wifi_score_params";
 
-       /**
+        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
         * A value of N means that we will make N+1 connection attempts in all.
         */
-       public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+        @Readable
+        public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
 
-       /**
+        /**
         * Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile
         * data connectivity to be established after a disconnect from Wi-Fi.
         */
-       public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
+        @Readable
+        public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
            "wifi_mobile_data_transition_wakelock_timeout_ms";
 
-       /**
+        /**
         * This setting controls whether WiFi configurations created by a Device Owner app
         * should be locked down (that is, be editable or removable only by the Device Owner App,
         * not even by Settings app).
@@ -11076,10 +11840,11 @@
         * are locked down. Value of zero means they are not. Default value in the absence of
         * actual value to this setting is 0.
         */
-       public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN =
+        @Readable
+        public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN =
                "wifi_device_owner_configs_lockdown";
 
-       /**
+        /**
         * The operational wifi frequency band
         * Set to one of {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
         * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ} or
@@ -11087,18 +11852,21 @@
         *
         * @hide
         */
-       public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band";
+        @Readable
+        public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band";
 
-       /**
+        /**
         * The Wi-Fi peer-to-peer device name
         * @hide
         * @deprecated Use {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel, String,
         * WifiP2pManager.ActionListener)} for setting the value and
         * {@link android.net.wifi.p2p.WifiP2pDevice#deviceName} for query.
         */
-       public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
+        @Deprecated
+        @Readable
+        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
-       /**
+        /**
         * Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect
         * from an ephemeral network if there is no BSSID for that network with a non-null score that
         * has been seen in this time period.
@@ -11107,53 +11875,60 @@
         * for a non-null score from the currently connected or target BSSID.
         * @hide
         */
-       public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS =
+        @Readable
+        public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS =
                "wifi_ephemeral_out_of_range_timeout_ms";
 
-       /**
+        /**
         * The number of milliseconds to delay when checking for data stalls during
         * non-aggressive detection. (screen is turned off.)
         * @hide
         */
-       public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
+        @Readable
+        public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
                "data_stall_alarm_non_aggressive_delay_in_ms";
 
-       /**
+        /**
         * The number of milliseconds to delay when checking for data stalls during
         * aggressive detection. (screen on or suspected data stall)
         * @hide
         */
-       public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
+        @Readable
+        public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
                "data_stall_alarm_aggressive_delay_in_ms";
 
-       /**
+        /**
         * The number of milliseconds to allow the provisioning apn to remain active
         * @hide
         */
-       public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS =
+        @Readable
+        public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS =
                "provisioning_apn_alarm_delay_in_ms";
 
-       /**
+        /**
         * The interval in milliseconds at which to check gprs registration
         * after the first registration mismatch of gprs and voice service,
         * to detect possible data network registration problems.
         *
         * @hide
         */
-       public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
+        @Readable
+        public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
                "gprs_register_check_period_ms";
 
-       /**
+        /**
         * Nonzero causes Log.wtf() to crash.
         * @hide
         */
-       public static final String WTF_IS_FATAL = "wtf_is_fatal";
+        @Readable
+        public static final String WTF_IS_FATAL = "wtf_is_fatal";
 
-       /**
+        /**
         * Ringer mode. This is used internally, changing this value will not
         * change the ringer mode. See AudioManager.
         */
-       public static final String MODE_RINGER = "mode_ringer";
+        @Readable
+        public static final String MODE_RINGER = "mode_ringer";
 
         /**
          * Overlay display devices setting.
@@ -11194,6 +11969,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
 
         /**
@@ -11202,10 +11978,12 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold";
 
         /** @hide */
+        @Readable
         public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold";
 
         /**
@@ -11217,6 +11995,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SEND_ACTION_APP_ERROR = "send_action_app_error";
 
         /**
@@ -11224,6 +12003,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_AGE_SECONDS = "dropbox_age_seconds";
 
         /**
@@ -11232,6 +12012,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_MAX_FILES = "dropbox_max_files";
 
         /**
@@ -11240,6 +12021,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_QUOTA_KB = "dropbox_quota_kb";
 
         /**
@@ -11248,6 +12030,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_QUOTA_PERCENT = "dropbox_quota_percent";
 
         /**
@@ -11256,6 +12039,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_RESERVE_PERCENT = "dropbox_reserve_percent";
 
         /**
@@ -11263,6 +12047,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DROPBOX_TAG_PREFIX = "dropbox:";
 
         /**
@@ -11273,6 +12058,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ERROR_LOGCAT_PREFIX = "logcat_for_";
 
         /**
@@ -11286,6 +12072,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MAX_ERROR_BYTES_PREFIX = "max_error_bytes_for_";
 
         /**
@@ -11294,6 +12081,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SYS_FREE_STORAGE_LOG_INTERVAL = "sys_free_storage_log_interval";
 
         /**
@@ -11303,6 +12091,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 DISK_FREE_CHANGE_REPORTING_THRESHOLD = "disk_free_change_reporting_threshold";
 
@@ -11315,6 +12104,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage";
 
@@ -11326,6 +12116,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
 
@@ -11336,6 +12127,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";
 
@@ -11345,6 +12137,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
 
@@ -11354,6 +12147,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
 
@@ -11363,6 +12157,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 SYNC_MAX_RETRY_DELAY_IN_SECONDS = "sync_max_retry_delay_in_seconds";
 
@@ -11372,6 +12167,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
 
 
@@ -11381,7 +12177,7 @@
          *
          * @hide
          */
-
+        @Readable
         public static final String CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS =
                 "connectivity_sampling_interval_in_seconds";
 
@@ -11391,6 +12187,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String PAC_CHANGE_DELAY = "pac_change_delay";
 
         /**
@@ -11423,6 +12220,7 @@
          * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
 
         /**
@@ -11433,6 +12231,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String
                 CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
 
@@ -11443,6 +12242,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_SERVER = "captive_portal_server";
 
         /**
@@ -11451,6 +12251,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
 
         /**
@@ -11459,6 +12260,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
 
         /**
@@ -11467,6 +12269,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
 
         /**
@@ -11475,6 +12278,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
                 "captive_portal_other_fallback_urls";
 
@@ -11484,6 +12288,7 @@
          * by "@@,@@".
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
                 "captive_portal_fallback_probe_specs";
 
@@ -11494,6 +12299,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
 
         /**
@@ -11502,6 +12308,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
 
         /**
@@ -11509,6 +12316,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK =
                 "data_stall_recovery_on_bad_network";
 
@@ -11517,6 +12325,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS =
                 "min_duration_between_recovery_steps";
         /**
@@ -11524,6 +12333,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String NSD_ON = "nsd_on";
 
         /**
@@ -11531,6 +12341,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SET_INSTALL_LOCATION = "set_install_location";
 
         /**
@@ -11540,6 +12351,7 @@
          * 2 = sdcard
          * @hide
          */
+        @Readable
         public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
 
         /**
@@ -11548,6 +12360,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 INET_CONDITION_DEBOUNCE_UP_DELAY = "inet_condition_debounce_up_delay";
 
@@ -11557,10 +12370,12 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 INET_CONDITION_DEBOUNCE_DOWN_DELAY = "inet_condition_debounce_down_delay";
 
         /** {@hide} */
+        @Readable
         public static final String
                 READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT = "read_external_storage_enforced_default";
 
@@ -11568,6 +12383,7 @@
          * Host name and port for global http proxy. Uses ':' seperator for
          * between host and port.
          */
+        @Readable
         public static final String HTTP_PROXY = "http_proxy";
 
         /**
@@ -11575,6 +12391,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
 
         /**
@@ -11582,6 +12399,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
 
         /**
@@ -11593,6 +12411,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String
                 GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list";
 
@@ -11600,6 +12419,7 @@
          * The location PAC File for the proxy.
          * @hide
          */
+        @Readable
         public static final String
                 GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
 
@@ -11609,6 +12429,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
 
         /**
@@ -11616,6 +12437,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DEFAULT_DNS_SERVER = "default_dns_server";
 
         /**
@@ -11628,11 +12450,13 @@
          *
          * @hide
          */
+        @Readable
         public static final String PRIVATE_DNS_MODE = "private_dns_mode";
 
         /**
          * @hide
          */
+        @Readable
         public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
 
         /**
@@ -11644,46 +12468,60 @@
           *
           * {@hide}
           */
+        @Readable
         public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
 
 
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_HEADSET_PRIORITY_PREFIX = "bluetooth_headset_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_";
         /** {@hide} */
+        @Readable
         public static final String BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX =
                 "bluetooth_a2dp_supports_optional_codecs_";
         /** {@hide} */
+        @Readable
         public static final String BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX =
                 "bluetooth_a2dp_optional_codecs_enabled_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
         /** {@hide} */
+        @Readable
         public static final String
                 BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
 
@@ -11692,6 +12530,7 @@
          *
          * {@hide}
          */
+        @Readable
         public static final String
                 ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
 
@@ -11700,6 +12539,7 @@
          *
          * {@hide}
          */
+        @Readable
         public static final String
                 RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
                 "radio_bug_wakelock_timeout_count_threshold";
@@ -11709,6 +12549,7 @@
          *
          * {@hide}
          */
+        @Readable
         public static final String
                 RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
                 "radio_bug_system_error_count_threshold";
@@ -11755,6 +12596,7 @@
          * @hide
          * @see com.android.server.am.ActivityManagerConstants
          */
+        @Readable
         public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants";
 
         /**
@@ -11763,6 +12605,7 @@
          * Default: 1
          * @hide
          */
+        @Readable
         public static final String ACTIVITY_STARTS_LOGGING_ENABLED
                 = "activity_starts_logging_enabled";
 
@@ -11772,6 +12615,7 @@
          * Default: 1
          * @hide
          */
+        @Readable
         public static final String FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED =
                 "foreground_service_starts_logging_enabled";
 
@@ -11779,6 +12623,7 @@
          * @hide
          * @see com.android.server.appbinding.AppBindingConstants
          */
+        @Readable
         public static final String APP_BINDING_CONSTANTS = "app_binding_constants";
 
         /**
@@ -11801,6 +12646,7 @@
          * @see com.android.server.AppOpsService.Constants
          */
         @TestApi
+        @Readable
         public static final String APP_OPS_CONSTANTS = "app_ops_constants";
 
         /**
@@ -11836,6 +12682,7 @@
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @TestApi
+        @Readable
         public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
 
         /**
@@ -11853,6 +12700,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
                 "battery_saver_device_specific_constants";
 
@@ -11882,6 +12730,7 @@
          * </pre>
          * @hide
          */
+        @Readable
         public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
 
         /**
@@ -11907,6 +12756,7 @@
          * </pre>
          * @hide
          */
+        @Readable
         public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
 
         /**
@@ -11914,6 +12764,7 @@
          * current version is 1.
          * @hide
          */
+        @Readable
         public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version";
 
         /**
@@ -11921,6 +12772,7 @@
          * {@link android.app.StatsManager}.
          * @hide
          */
+        @Readable
         public static final String ANOMALY_CONFIG = "anomaly_config";
 
         /**
@@ -11940,6 +12792,7 @@
          * </pre>
          * @hide
          */
+        @Readable
         public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
 
         /**
@@ -11950,6 +12803,7 @@
         * Any other value defaults to enabled.
         * @hide
         */
+        @Readable
         public static final String SYS_UIDCPUPOWER = "sys_uidcpupower";
 
         /**
@@ -11961,6 +12815,7 @@
         * Any other value defaults to disabled.
         * @hide
         */
+        @Readable
         public static final String SYS_TRACED = "sys_traced";
 
         /**
@@ -11969,6 +12824,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String FPS_DEVISOR = "fps_divisor";
 
         /**
@@ -11978,6 +12834,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
 
         /**
@@ -11992,6 +12849,7 @@
          * Need to reboot the device for this setting to take effect.
          * @hide
          */
+        @Readable
         public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source";
 
         /**
@@ -11999,6 +12857,7 @@
          * 0 = disable, 1 = enable.
          * @hide
          */
+        @Readable
         public static final String ART_VERIFIER_VERIFY_DEBUGGABLE =
                 "art_verifier_verify_debuggable";
 
@@ -12019,6 +12878,7 @@
          * @hide
          * @see com.android.server.power.PowerManagerConstants
          */
+        @Readable
         public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
 
         /**
@@ -12044,6 +12904,7 @@
          * @hide
          * @see com.android.server.pm.ShortcutService.ConfigConstants
          */
+        @Readable
         public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
 
         /**
@@ -12061,6 +12922,7 @@
          * @hide
          * see also com.android.server.devicepolicy.DevicePolicyConstants
          */
+        @Readable
         public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants";
 
         /**
@@ -12097,6 +12959,7 @@
          * @hide
          * see also android.view.textclassifier.TextClassificationConstants
          */
+        @Readable
         public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
 
         /**
@@ -12121,6 +12984,7 @@
          * @hide
          * see also com.android.internal.os.BatteryStatsImpl.Constants
          */
+        @Readable
         public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
 
         /**
@@ -12131,6 +12995,7 @@
          * @hide
          * @see com.android.server.content.SyncManagerConstants
          */
+        @Readable
         public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
 
         /**
@@ -12150,6 +13015,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants";
 
         /**
@@ -12160,6 +13026,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants";
 
         /**
@@ -12170,6 +13037,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants";
 
         /**
@@ -12182,6 +13050,7 @@
          * @see #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
          */
         @SystemApi
+        @Readable
         public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
 
         /**
@@ -12192,6 +13061,7 @@
          * @hide
          * @see #APP_STANDBY_ENABLED
          */
+        @Readable
         public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED =
                 "adaptive_battery_management_enabled";
 
@@ -12203,6 +13073,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
 
         /**
@@ -12220,6 +13091,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String APP_AUTO_RESTRICTION_ENABLED =
                 "app_auto_restriction_enabled";
 
@@ -12229,6 +13101,7 @@
          * Default: 1
          * @hide
          */
+        @Readable
         public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
 
         /**
@@ -12237,6 +13110,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
                 = "forced_app_standby_for_small_battery_enabled";
 
@@ -12246,6 +13120,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
                 = "user_absent_radios_off_for_small_battery_enabled";
 
@@ -12255,6 +13130,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String USER_ABSENT_TOUCH_OFF_FOR_SMALL_BATTERY_ENABLED
                 = "user_absent_touch_off_for_small_battery_enabled";
 
@@ -12264,6 +13140,7 @@
          * Default: 1
          * @hide
          */
+        @Readable
         public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED
                 = "wifi_on_when_proxy_disconnected";
 
@@ -12282,6 +13159,7 @@
          * Type: string
          * @hide
          */
+        @Readable
         public static final String TIME_ONLY_MODE_CONSTANTS
                 = "time_only_mode_constants";
 
@@ -12292,6 +13170,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled";
 
         /**
@@ -12300,6 +13179,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
 
         /**
@@ -12308,6 +13188,7 @@
          * Default: 1
          * @hide
          */
+        @Readable
         public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
                 "show_hidden_icon_apps_enabled";
 
@@ -12317,6 +13198,7 @@
          * Default: 0
          * @hide
          */
+        @Readable
         public static final String SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED =
                 "show_new_app_installed_notification_enabled";
 
@@ -12330,6 +13212,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
 
         /**
@@ -12351,6 +13234,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ADB_ALLOWED_CONNECTION_TIME =
                 "adb_allowed_connection_time";
 
@@ -12358,12 +13242,14 @@
          * Scaling factor for normal window animations. Setting to 0 will
          * disable window animations.
          */
+        @Readable
         public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale";
 
         /**
          * Scaling factor for activity transition animations. Setting to 0 will
          * disable window animations.
          */
+        @Readable
         public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
 
         /**
@@ -12371,6 +13257,7 @@
          * start delay and duration of all such animations. Setting to 0 will
          * cause animations to end immediately. The default value is 1.
          */
+        @Readable
         public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
 
         /**
@@ -12379,6 +13266,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations";
 
         /**
@@ -12387,6 +13275,7 @@
          * TODO: remove this settings before code freeze (bug/1907571)
          * @hide
          */
+        @Readable
         public static final String COMPATIBILITY_MODE = "compatibility_mode";
 
         /**
@@ -12396,6 +13285,7 @@
          *                 2 = Vibrate
          * @hide
          */
+        @Readable
         public static final String EMERGENCY_TONE = "emergency_tone";
 
         /**
@@ -12404,6 +13294,7 @@
          * boolean (1 or 0).
          * @hide
          */
+        @Readable
         public static final String CALL_AUTO_RETRY = "call_auto_retry";
 
         /**
@@ -12411,6 +13302,7 @@
          * The value is a boolean (1 or 0).
          * @hide
          */
+        @Readable
         public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
 
         /**
@@ -12420,6 +13312,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS =
                 "enable_automatic_system_server_heap_dumps";
 
@@ -12428,18 +13321,21 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String PREFERRED_NETWORK_MODE =
                 "preferred_network_mode";
 
         /**
          * Name of an application package to be debugged.
          */
+        @Readable
         public static final String DEBUG_APP = "debug_app";
 
         /**
          * If 1, when launching DEBUG_APP it will wait for the debugger before
          * starting user code.  If 0, it will run normally.
          */
+        @Readable
         public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
 
         /**
@@ -12448,12 +13344,14 @@
          * 1 = yes
          * @hide
          */
+        @Readable
         public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
 
         /**
          * App allowed to load GPU debug layers
          * @hide
          */
+        @Readable
         public static final String GPU_DEBUG_APP = "gpu_debug_app";
 
         /**
@@ -12461,6 +13359,7 @@
          * to dumpable apps that opt-in.
          * @hide
          */
+        @Readable
         public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package";
 
         /**
@@ -12468,12 +13367,14 @@
          * The value is a boolean (1 or 0).
          * @hide
          */
+        @Readable
         public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle";
 
         /**
          * List of PKGs that have an OpenGL driver selected
          * @hide
          */
+        @Readable
         public static final String ANGLE_GL_DRIVER_SELECTION_PKGS =
                 "angle_gl_driver_selection_pkgs";
 
@@ -12481,6 +13382,7 @@
          * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
          * @hide
          */
+        @Readable
         public static final String ANGLE_GL_DRIVER_SELECTION_VALUES =
                 "angle_gl_driver_selection_values";
 
@@ -12488,6 +13390,7 @@
          * List of package names that should check ANGLE rules
          * @hide
          */
+        @Readable
         public static final String ANGLE_ALLOWLIST = "angle_allowlist";
 
         /**
@@ -12497,6 +13400,7 @@
          * e.g. feature1:feature2:feature3,feature1:feature3:feature5
          * @hide
          */
+        @Readable
         public static final String ANGLE_EGL_FEATURES = "angle_egl_features";
 
         /**
@@ -12504,6 +13408,7 @@
          * The value is a boolean (1 or 0).
          * @hide
          */
+        @Readable
         public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box";
 
         /**
@@ -12514,6 +13419,7 @@
          * 3 = All Apps use system graphics driver
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_ALL_APPS = "updatable_driver_all_apps";
 
         /**
@@ -12521,6 +13427,7 @@
          * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS =
                 "updatable_driver_production_opt_in_apps";
 
@@ -12529,6 +13436,7 @@
          * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS =
                 "updatable_driver_prerelease_opt_in_apps";
 
@@ -12537,6 +13445,7 @@
          * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS =
                 "updatable_driver_production_opt_out_apps";
 
@@ -12544,6 +13453,7 @@
          * Apps on the denylist that are forbidden to use updatable production driver.
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLIST =
                 "updatable_driver_production_denylist";
 
@@ -12552,6 +13462,7 @@
          * updatable production driver.
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLISTS =
                 "updatable_driver_production_denylists";
 
@@ -12561,6 +13472,7 @@
          * i.e. <apk1>,<apk2>,...,<apkN>
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST =
                 "updatable_driver_production_allowlist";
 
@@ -12570,6 +13482,7 @@
          * i.e. <lib1>:<lib2>:...:<libN>
          * @hide
          */
+        @Readable
         public static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES =
                 "updatable_driver_sphal_libraries";
 
@@ -12578,6 +13491,7 @@
          * i.e. <layer1>:<layer2>:...:<layerN>
          * @hide
          */
+        @Readable
         public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
 
         /**
@@ -12585,12 +13499,14 @@
          * i.e. <layer1>:<layer2>:...:<layerN>
          * @hide
          */
+        @Readable
         public static final String GPU_DEBUG_LAYERS_GLES = "gpu_debug_layers_gles";
 
         /**
          * Addition app for GPU layer discovery
          * @hide
          */
+        @Readable
         public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app";
 
         /**
@@ -12600,6 +13516,7 @@
          * {@link android.os.Build.VERSION_CODES#N_MR1}.
          */
         @Deprecated
+        @Readable
         public static final String SHOW_PROCESSES = "show_processes";
 
         /**
@@ -12607,6 +13524,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String LOW_POWER_MODE = "low_power";
 
         /**
@@ -12615,6 +13533,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
 
         /**
@@ -12624,6 +13543,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL =
                 "low_power_sticky_auto_disable_level";
 
@@ -12633,6 +13553,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED =
                 "low_power_sticky_auto_disable_enabled";
 
@@ -12646,6 +13567,7 @@
          * @see android.os.PowerManager#getPowerSaveModeTrigger()
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
 
         /**
@@ -12656,6 +13578,7 @@
          *  @hide
          */
         @TestApi
+        @Readable
         public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
 
         /**
@@ -12666,6 +13589,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
                 "dynamic_power_savings_disable_threshold";
 
@@ -12676,6 +13600,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
 
         /**
@@ -12687,6 +13612,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String TIME_REMAINING_ESTIMATE_MILLIS =
                 "time_remaining_estimate_millis";
 
@@ -12700,6 +13626,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE =
                 "time_remaining_estimate_based_on_usage";
 
@@ -12712,6 +13639,7 @@
          * @hide
          */
         @Deprecated
+        @Readable
         public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge";
 
         /**
@@ -12723,6 +13651,7 @@
          * @deprecated No longer needed due to {@link PowerManager#getBatteryDischargePrediction}.
          */
         @Deprecated
+        @Readable
         public static final String BATTERY_ESTIMATES_LAST_UPDATE_TIME =
                 "battery_estimates_last_update_time";
 
@@ -12732,12 +13661,14 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max";
 
         /**
          * See com.android.settingslib.fuelgauge.BatterySaverUtils.
          * @hide
          */
+        @Readable
         public static final String LOW_POWER_MODE_SUGGESTION_PARAMS =
                 "low_power_mode_suggestion_params";
 
@@ -12746,6 +13677,7 @@
          * processes as soon as they are no longer needed.  If 0, the normal
          * extended lifetime is used.
          */
+        @Readable
         public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
 
         /**
@@ -12755,6 +13687,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
 
         /**
@@ -12763,6 +13696,7 @@
          *      1 = enabled
          * @hide
          */
+        @Readable
         public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled";
 
         /**
@@ -12822,6 +13756,7 @@
          * ENCODED_SURROUND_OUTPUT_MANUAL
          * @hide
          */
+        @Readable
         public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
 
         /**
@@ -12833,6 +13768,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS =
                 "encoded_surround_output_enabled_formats";
 
@@ -12840,36 +13776,42 @@
          * Persisted safe headphone volume management state by AudioService
          * @hide
          */
+        @Readable
         public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state";
 
         /**
          * URL for tzinfo (time zone) updates
          * @hide
          */
+        @Readable
         public static final String TZINFO_UPDATE_CONTENT_URL = "tzinfo_content_url";
 
         /**
          * URL for tzinfo (time zone) update metadata
          * @hide
          */
+        @Readable
         public static final String TZINFO_UPDATE_METADATA_URL = "tzinfo_metadata_url";
 
         /**
          * URL for selinux (mandatory access control) updates
          * @hide
          */
+        @Readable
         public static final String SELINUX_UPDATE_CONTENT_URL = "selinux_content_url";
 
         /**
          * URL for selinux (mandatory access control) update metadata
          * @hide
          */
+        @Readable
         public static final String SELINUX_UPDATE_METADATA_URL = "selinux_metadata_url";
 
         /**
          * URL for sms short code updates
          * @hide
          */
+        @Readable
         public static final String SMS_SHORT_CODES_UPDATE_CONTENT_URL =
                 "sms_short_codes_content_url";
 
@@ -12877,6 +13819,7 @@
          * URL for sms short code update metadata
          * @hide
          */
+        @Readable
         public static final String SMS_SHORT_CODES_UPDATE_METADATA_URL =
                 "sms_short_codes_metadata_url";
 
@@ -12884,30 +13827,35 @@
          * URL for apn_db updates
          * @hide
          */
+        @Readable
         public static final String APN_DB_UPDATE_CONTENT_URL = "apn_db_content_url";
 
         /**
          * URL for apn_db update metadata
          * @hide
          */
+        @Readable
         public static final String APN_DB_UPDATE_METADATA_URL = "apn_db_metadata_url";
 
         /**
          * URL for cert pinlist updates
          * @hide
          */
+        @Readable
         public static final String CERT_PIN_UPDATE_CONTENT_URL = "cert_pin_content_url";
 
         /**
          * URL for cert pinlist updates
          * @hide
          */
+        @Readable
         public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url";
 
         /**
          * URL for intent firewall updates
          * @hide
          */
+        @Readable
         public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL =
                 "intent_firewall_content_url";
 
@@ -12915,6 +13863,7 @@
          * URL for intent firewall update metadata
          * @hide
          */
+        @Readable
         public static final String INTENT_FIREWALL_UPDATE_METADATA_URL =
                 "intent_firewall_metadata_url";
 
@@ -12922,18 +13871,21 @@
          * URL for lang id model updates
          * @hide
          */
+        @Readable
         public static final String LANG_ID_UPDATE_CONTENT_URL = "lang_id_content_url";
 
         /**
          * URL for lang id model update metadata
          * @hide
          */
+        @Readable
         public static final String LANG_ID_UPDATE_METADATA_URL = "lang_id_metadata_url";
 
         /**
          * URL for smart selection model updates
          * @hide
          */
+        @Readable
         public static final String SMART_SELECTION_UPDATE_CONTENT_URL =
                 "smart_selection_content_url";
 
@@ -12941,6 +13893,7 @@
          * URL for smart selection model update metadata
          * @hide
          */
+        @Readable
         public static final String SMART_SELECTION_UPDATE_METADATA_URL =
                 "smart_selection_metadata_url";
 
@@ -12948,6 +13901,7 @@
          * URL for conversation actions model updates
          * @hide
          */
+        @Readable
         public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL =
                 "conversation_actions_content_url";
 
@@ -12955,6 +13909,7 @@
          * URL for conversation actions model update metadata
          * @hide
          */
+        @Readable
         public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL =
                 "conversation_actions_metadata_url";
 
@@ -12962,12 +13917,14 @@
          * SELinux enforcement status. If 0, permissive; if 1, enforcing.
          * @hide
          */
+        @Readable
         public static final String SELINUX_STATUS = "selinux_status";
 
         /**
          * Developer setting to force RTL layout.
          * @hide
          */
+        @Readable
         public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl";
 
         /**
@@ -12978,6 +13935,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout";
 
         /**
@@ -12987,6 +13945,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String WIFI_BOUNCE_DELAY_OVERRIDE_MS = "wifi_bounce_delay_override_ms";
 
         /**
@@ -12996,6 +13955,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String POLICY_CONTROL = "policy_control";
 
         /**
@@ -13003,6 +13963,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EMULATE_DISPLAY_CUTOUT = "emulate_display_cutout";
 
         /** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_OFF = 0;
@@ -13013,6 +13974,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BLOCKED_SLICES = "blocked_slices";
 
         /**
@@ -13022,6 +13984,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ZEN_MODE = "zen_mode";
 
         /** @hide */
@@ -13061,6 +14024,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ZEN_MODE_RINGER_LEVEL = "zen_mode_ringer_level";
 
         /**
@@ -13069,6 +14033,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
 
         /**
@@ -13098,6 +14063,7 @@
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+        @Readable
         public static final String HEADS_UP_NOTIFICATIONS_ENABLED =
                 "heads_up_notifications_enabled";
 
@@ -13111,6 +14077,7 @@
         /**
          * The name of the device
          */
+        @Readable
         public static final String DEVICE_NAME = "device_name";
 
         /**
@@ -13119,6 +14086,7 @@
          * Type: int (0 for false, 1 for true)
          * @hide
          */
+        @Readable
         public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
 
         /**
@@ -13130,6 +14098,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
 
         /**
@@ -13143,6 +14112,7 @@
          * {@link android.provider.Telephony.SimInfo#COLUMN_ENHANCED_4G_MODE_ENABLED} instead.
          */
         @Deprecated
+        @Readable
         public static final String ENHANCED_4G_MODE_ENABLED =
                 Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED;
 
@@ -13155,6 +14125,7 @@
          * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_VT_IMS_ENABLED} instead.
          */
         @Deprecated
+        @Readable
         public static final String VT_IMS_ENABLED = Telephony.SimInfo.COLUMN_VT_IMS_ENABLED;
 
         /**
@@ -13167,6 +14138,7 @@
          * {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ENABLED} instead.
          */
         @Deprecated
+        @Readable
         public static final String WFC_IMS_ENABLED = Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED;
 
         /**
@@ -13178,6 +14150,7 @@
          * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_MODE} instead.
          */
         @Deprecated
+        @Readable
         public static final String WFC_IMS_MODE = Telephony.SimInfo.COLUMN_WFC_IMS_MODE;
 
         /**
@@ -13190,6 +14163,7 @@
          * instead.
          */
         @Deprecated
+        @Readable
         public static final String WFC_IMS_ROAMING_MODE =
                 Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
 
@@ -13203,6 +14177,7 @@
          * instead
          */
         @Deprecated
+        @Readable
         public static final String WFC_IMS_ROAMING_ENABLED =
                 Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
 
@@ -13213,6 +14188,7 @@
          * Type: int (0 for false, 1 for true)
          * @hide
          */
+        @Readable
         public static final String LTE_SERVICE_FORCED = "lte_service_forced";
 
 
@@ -13222,6 +14198,7 @@
          * See WindowManagerPolicy.WindowManagerFuncs
          * @hide
          */
+        @Readable
         public static final String LID_BEHAVIOR = "lid_behavior";
 
         /**
@@ -13230,6 +14207,7 @@
          * Type: int
          * @hide
          */
+        @Readable
         public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES =
                 "ephemeral_cookie_max_size_bytes";
 
@@ -13241,6 +14219,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
 
         /**
@@ -13251,6 +14230,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled";
 
         /**
@@ -13259,6 +14239,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
                 "installed_instant_app_min_cache_period";
 
@@ -13268,6 +14249,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
                 "installed_instant_app_max_cache_period";
 
@@ -13277,6 +14259,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
                 "uninstalled_instant_app_min_cache_period";
 
@@ -13286,6 +14269,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
                 "uninstalled_instant_app_max_cache_period";
 
@@ -13295,6 +14279,7 @@
          * Type: long
          * @hide
          */
+        @Readable
         public static final String UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
                 "unused_static_shared_lib_min_cache_period";
 
@@ -13304,6 +14289,7 @@
          * Type: int
          * @hide
          */
+        @Readable
         public static final String ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED =
                 "allow_user_switching_when_system_user_locked";
 
@@ -13312,6 +14298,7 @@
          * <p>
          * Type: int
          */
+        @Readable
         public static final String BOOT_COUNT = "boot_count";
 
         /**
@@ -13322,6 +14309,7 @@
          * before the user restrictions are loaded.
          * @hide
          */
+        @Readable
         public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
 
         /**
@@ -13333,6 +14321,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String DEVICE_DEMO_MODE = "device_demo_mode";
 
         /**
@@ -13342,6 +14331,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
 
         /**
@@ -13352,6 +14342,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason";
 
         /**
@@ -13362,6 +14353,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String DATABASE_CREATION_BUILDID = "database_creation_buildid";
 
         /**
@@ -13370,6 +14362,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled";
 
         /**
@@ -13377,6 +14370,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
                 "location_settings_link_to_permissions_enabled";
 
@@ -13387,6 +14381,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
                 "euicc_removing_invisible_profiles_timeout_millis";
 
@@ -13396,6 +14391,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EUICC_FACTORY_RESET_TIMEOUT_MILLIS =
                 "euicc_factory_reset_timeout_millis";
 
@@ -13405,6 +14401,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
                 "euicc_switch_slot_timeout_millis";
 
@@ -13414,6 +14411,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
                 "enable_multi_slot_timeout_millis";
 
@@ -13423,6 +14421,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String STORAGE_SETTINGS_CLOBBER_THRESHOLD =
                 "storage_settings_clobber_threshold";
 
@@ -13432,6 +14431,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION =
                 "override_settings_provider_restore_any_version";
         /**
@@ -13442,6 +14442,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String CHAINED_BATTERY_ATTRIBUTION_ENABLED =
                 "chained_battery_attribution_enabled";
 
@@ -13454,6 +14455,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT =
                 "enable_adb_incremental_install_default";
 
@@ -13471,6 +14473,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
                 "autofill_compat_mode_allowed_packages";
 
@@ -13484,6 +14487,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level";
 
         /**
@@ -13491,6 +14495,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size";
 
         /**
@@ -13499,6 +14504,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
 
         /**
@@ -13507,6 +14513,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
                 "hidden_api_blacklist_exemptions";
 
@@ -13519,14 +14526,16 @@
          * @hide
          */
         @TestApi
+        @Readable
         public static final String HIDDEN_API_POLICY = "hidden_api_policy";
 
-         /**
+        /**
          * Flag for forcing {@link com.android.server.compat.OverrideValidatorImpl}
          * to consider this a non-debuggable build.
          *
          * @hide
          */
+        @Readable
         public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
                 "force_non_debuggable_final_build_for_compat";
 
@@ -13536,6 +14545,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
 
         /**
@@ -13544,6 +14554,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
                 "sound_trigger_detection_service_op_timeout";
 
@@ -13553,6 +14564,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
                 "max_sound_trigger_detection_service_ops_per_day";
 
@@ -13560,6 +14572,7 @@
          * Indicates whether aware is available in the current location.
          * @hide
          */
+        @Readable
         public static final String AWARE_ALLOWED = "aware_allowed";
 
         /**
@@ -13568,6 +14581,7 @@
          * Used by PhoneWindowManager.
          * @hide
          */
+        @Readable
         public static final String POWER_BUTTON_LONG_PRESS =
                 "power_button_long_press";
 
@@ -13577,31 +14591,10 @@
          * Used by PhoneWindowManager.
          * @hide
          */
+        @Readable
         public static final String POWER_BUTTON_VERY_LONG_PRESS =
                 "power_button_very_long_press";
 
-
-        /**
-         * Keyguard should be on the left hand side of the screen, for wide screen layouts.
-         *
-         * @hide
-         */
-        public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
-
-        /**
-         * Keyguard should be on the right hand side of the screen, for wide screen layouts.
-         *
-         * @hide
-         */
-        public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
-        /**
-         * In one handed mode, which side the keyguard should be on. Allowable values are one of
-         * the ONE_HANDED_KEYGUARD_SIDE_* constants.
-         *
-         * @hide
-         */
-        public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
-
         /**
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
@@ -13624,7 +14617,8 @@
                     CONTENT_URI,
                     CALL_METHOD_GET_GLOBAL,
                     CALL_METHOD_PUT_GLOBAL,
-                    sProviderHolder);
+                    sProviderHolder,
+                    Global.class);
 
         // Certain settings have been moved from global to the per-user secure namespace
         @UnsupportedAppUsage
@@ -13653,6 +14647,11 @@
             sNameValueCache.clearGenerationTrackerForTest();
         }
 
+        /** @hide */
+        public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+            getPublicSettingsForClass(Global.class, allKeys, readableKeys);
+        }
+
         /**
          * Look up a name in the database.
          * @param resolver to access the database with
@@ -14062,6 +15061,7 @@
           * Subscription Id to be used for voice call on a multi sim device.
           * @hide
           */
+        @Readable
         public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call";
 
         /**
@@ -14070,18 +15070,21 @@
           * @hide
           */
         @UnsupportedAppUsage
+        @Readable
         public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt";
 
         /**
           * Subscription Id to be used for data call on a multi sim device.
           * @hide
           */
+        @Readable
         public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call";
 
         /**
           * Subscription Id to be used for SMS on a multi sim device.
           * @hide
           */
+        @Readable
         public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms";
 
         /**
@@ -14089,6 +15092,7 @@
           * The value 1 - enable, 0 - disable
           * @hide
           */
+        @Readable
         public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
 
         /** User preferred subscriptions setting.
@@ -14098,6 +15102,7 @@
           * @hide
          */
         @UnsupportedAppUsage
+        @Readable
         public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1",
                 "user_preferred_sub2","user_preferred_sub3"};
 
@@ -14105,6 +15110,7 @@
          * Which subscription is enabled for a physical slot.
          * @hide
          */
+        @Readable
         public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
 
         /**
@@ -14112,6 +15118,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
 
         /**
@@ -14119,6 +15126,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
 
         /**
@@ -14128,12 +15136,14 @@
          * @removed
          */
         @Deprecated
+        @Readable
         public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
 
         /**
          * Whether to enable contacts metadata syncing or not
          * The value 1 - enable, 0 - disable
          */
+        @Readable
         public static final String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled";
 
         /**
@@ -14141,6 +15151,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
 
         /**
@@ -14149,6 +15160,7 @@
          * Should be a float, and includes updates only.
          * @hide
          */
+        @Readable
         public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
 
         /**
@@ -14157,6 +15169,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
                 "show_notification_channel_warnings";
 
@@ -14164,6 +15177,7 @@
          * Whether cell is enabled/disabled
          * @hide
          */
+        @Readable
         public static final String CELL_ON = "cell_on";
 
         /**
@@ -14192,30 +15206,35 @@
          * Whether to show the high temperature warning notification.
          * @hide
          */
+        @Readable
         public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning";
 
         /**
          * Whether to show the usb high temperature alarm notification.
          * @hide
          */
+        @Readable
         public static final String SHOW_USB_TEMPERATURE_ALARM = "show_usb_temperature_alarm";
 
         /**
          * Temperature at which the high temperature warning notification should be shown.
          * @hide
          */
+        @Readable
         public static final String WARNING_TEMPERATURE = "warning_temperature";
 
         /**
          * Whether the diskstats logging task is enabled/disabled.
          * @hide
          */
+        @Readable
         public static final String ENABLE_DISKSTATS_LOGGING = "enable_diskstats_logging";
 
         /**
          * Whether the cache quota calculation task is enabled/disabled.
          * @hide
          */
+        @Readable
         public static final String ENABLE_CACHE_QUOTA_CALCULATION =
                 "enable_cache_quota_calculation";
 
@@ -14223,6 +15242,7 @@
          * Whether the Deletion Helper no threshold toggle is available.
          * @hide
          */
+        @Readable
         public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE =
                 "enable_deletion_helper_no_threshold_toggle";
 
@@ -14243,6 +15263,7 @@
          * Options will be used in order up to the maximum allowed by the UI.
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_SNOOZE_OPTIONS =
                 "notification_snooze_options";
 
@@ -14254,6 +15275,7 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
+        @Readable
         public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled";
 
         /**
@@ -14265,6 +15287,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT =
                 "blocking_helper_dismiss_to_view_ratio";
 
@@ -14276,6 +15299,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit";
 
         /**
@@ -14303,6 +15327,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String SQLITE_COMPATIBILITY_WAL_FLAGS =
                 "sqlite_compatibility_wal_flags";
 
@@ -14312,6 +15337,7 @@
          * 1 = yes
          * @hide
          */
+        @Readable
         public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING =
                 "enable_gnss_raw_meas_full_tracking";
 
@@ -14323,6 +15349,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
                 "install_carrier_app_notification_persistent";
 
@@ -14334,6 +15361,7 @@
          * @hide
          */
         @SystemApi
+        @Readable
         public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
                 "install_carrier_app_notification_sleep_millis";
 
@@ -14343,6 +15371,7 @@
          * everything else is unspecified.
          * @hide
          */
+        @Readable
         public static final String ZRAM_ENABLED =
                 "zram_enabled";
 
@@ -14352,6 +15381,7 @@
          * "device_default" will let the system decide whether to enable the freezer or not
          * @hide
          */
+        @Readable
         public static final String CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer";
 
         /**
@@ -14374,6 +15404,7 @@
          * @see com.android.systemui.statusbar.policy.SmartReplyConstants
          * @hide
          */
+        @Readable
         public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS =
                 "smart_replies_in_notifications_flags";
 
@@ -14390,6 +15421,7 @@
          * </pre>
          * @hide
          */
+        @Readable
         public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS =
                 "smart_suggestions_in_notifications_flags";
 
@@ -14399,6 +15431,7 @@
          * @hide
          */
         @TestApi
+        @Readable
         @SuppressLint("NoSettingsProvider")
         public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
 
@@ -14406,6 +15439,7 @@
          * If nonzero, crash dialogs will show an option to restart the app.
          * @hide
          */
+        @Readable
         public static final String SHOW_RESTART_IN_CRASH_DIALOG = "show_restart_in_crash_dialog";
 
         /**
@@ -14413,6 +15447,7 @@
          * this app.
          * @hide
          */
+        @Readable
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
 
 
@@ -14468,6 +15503,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
                 "backup_agent_timeout_parameters";
 
@@ -14482,6 +15518,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist";
 
         /**
@@ -14493,6 +15530,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
                 "gnss_hal_location_request_duration_millis";
 
@@ -14509,6 +15547,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BINDER_CALLS_STATS = "binder_calls_stats";
 
         /**
@@ -14522,6 +15561,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String LOOPER_STATS = "looper_stats";
 
         /**
@@ -14536,6 +15576,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
 
         /**
@@ -14543,6 +15584,7 @@
          * reboot. The value "1" enables native flags health check; otherwise it's disabled.
          * @hide
          */
+        @Readable
         public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
                 "native_flags_health_check_enabled";
 
@@ -14552,6 +15594,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String APPOP_HISTORY_MODE = "mode";
 
         /**
@@ -14561,6 +15604,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String APPOP_HISTORY_BASE_INTERVAL_MILLIS = "baseIntervalMillis";
 
         /**
@@ -14569,6 +15613,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String APPOP_HISTORY_INTERVAL_MULTIPLIER = "intervalMultiplier";
 
         /**
@@ -14590,6 +15635,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String APPOP_HISTORY_PARAMETERS =
                 "appop_history_parameters";
 
@@ -14607,6 +15653,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String AUTO_REVOKE_PARAMETERS =
                 "auto_revoke_parameters";
 
@@ -14618,6 +15665,7 @@
          * @see com.android.internal.os.BatteryStatsImpl.Constants.KEY_BATTERY_CHARGED_DELAY_MS
          * @hide
          */
+        @Readable
         public static final String BATTERY_CHARGING_STATE_UPDATE_DELAY =
                 "battery_charging_state_update_delay";
 
@@ -14626,6 +15674,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS =
                 "text_classifier_action_model_params";
 
@@ -14639,6 +15688,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
                 "power_button_suppression_delay_after_gesture_wake";
 
@@ -14647,6 +15697,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String ADVANCED_BATTERY_USAGE_AMOUNT = "advanced_battery_usage_amount";
 
         /**
@@ -14661,6 +15712,7 @@
          * 2: always on - All 5G NSA tracking indications are on whether the screen is on or off.
          * @hide
          */
+        @Readable
         public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
                 "nr_nsa_tracking_screen_off_mode";
 
@@ -14671,6 +15723,7 @@
          * 1: Enabled
          * @hide
          */
+        @Readable
         public static final String SHOW_PEOPLE_SPACE = "show_people_space";
 
         /**
@@ -14681,6 +15734,7 @@
          * 2: All conversations
          * @hide
          */
+        @Readable
         public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
                 "people_space_conversation_type";
 
@@ -14691,6 +15745,7 @@
          * 1: Enabled
          * @hide
          */
+        @Readable
         public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
 
         /**
@@ -14705,6 +15760,7 @@
          * 1: Enabled (All apps will receive the new rules)
          * @hide
          */
+        @Readable
         public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules";
 
         /**
@@ -14719,6 +15775,7 @@
          * <p>See {@link android.app.Notification.DevFlags} for more details.
          * @hide
          */
+        @Readable
         public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION =
                 "fully_custom_view_notif_decoration";
 
@@ -14732,6 +15789,7 @@
          * <p>See {@link android.app.Notification.DevFlags} for more details.
          * @hide
          */
+        @Readable
         public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION =
                 "decorated_custom_view_notif_decoration";
 
@@ -14748,6 +15806,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
 
         /**
@@ -14773,6 +15832,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
                 "maximum_obscuring_opacity_for_touch";
 
@@ -14785,6 +15845,7 @@
          * 1: enabled
          * @hide
          */
+        @Readable
         public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
     }
 
@@ -14806,7 +15867,8 @@
                 CALL_METHOD_PUT_CONFIG,
                 CALL_METHOD_LIST_CONFIG,
                 CALL_METHOD_SET_ALL_CONFIG,
-                sProviderHolder);
+                sProviderHolder,
+                Config.class);
 
         /**
          * Look up a name in the database.
diff --git a/core/java/android/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java
index db879a7..1f1a66c 100644
--- a/core/java/android/rotationresolver/RotationResolverInternal.java
+++ b/core/java/android/rotationresolver/RotationResolverInternal.java
@@ -46,7 +46,6 @@
      *                 error is captured. {@link RotationResolverCallbackInternal}
      * @param proposedRotation the screen rotation that is proposed by the system.
      * @param currentRotation the current screen rotation.
-     * @param packageName the package name of the current activity that is running in foreground.
      * @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get
      *                      fulfilled within this amount of time. It will be discarded and the
      *                      callback will receive a failure result code {@link
@@ -55,8 +54,7 @@
      */
     public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
             @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
-            String packageName, @DurationMillisLong long timeoutMillis,
-            @NonNull CancellationSignal cancellationSignal);
+            @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal);
 
     /**
      * Internal interfaces for the rotation resolver callback.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index c39b8c5..a79b197 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -130,6 +130,15 @@
     public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
     public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+    public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+            Tag.RESET_SINCE_ID_ROTATION;     // KM_BOOL | 1004
+    public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+    public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+    public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+    public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+            Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+    public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+            Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
 
     // Algorithm values.
     public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -317,6 +326,10 @@
             ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
     public static final int KM_ERROR_DEVICE_LOCKED =
             ErrorCode.DEVICE_LOCKED; // -72;
+    public static final int KM_ERROR_MISSING_NOT_BEFORE =
+            ErrorCode.MISSING_NOT_BEFORE; // -80;
+    public static final int KM_ERROR_MISSING_NOT_AFTER =
+            ErrorCode.MISSING_NOT_AFTER; // -80;
     public static final int KM_ERROR_UNIMPLEMENTED =
             ErrorCode.UNIMPLEMENTED; // -100;
     public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 44daeff..2b7cb1d 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -41,7 +41,7 @@
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
 
-    // companion device managers only
+    // companion device managers and assistants only
     void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
     void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
 
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
      * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
      * reboot. The service should not assume any structure of the blob to be wrapped. The
      * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
-     * if it's unable to complete the action.
+     * if it's unable to complete the action due to retry-able errors (e.g network errors)
+     * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+     * (e.g corrupted blob).
      *
      * @param blob             The opaque blob with size on the order of 100 bytes.
      * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
      *                         this function after expiration should
      *                         fail.
      * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to wrap the blob successfully.
+     * @throws IOException if the implementation is unable to wrap the blob successfully due to
+     * retry-able errors.
      */
     @NonNull
     public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
      * operation would happen after reboot during direct boot mode (i.e before device is unlocked
      * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
      * and returns the result or throw {@link IOException} if it's unable to complete the action
-     * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
-     * stale.
+     * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+     * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
      *
      * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
      * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
-     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+     * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+     * due to retry-able errors.
      */
     @NonNull
     public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 94a6052..8e76e2f 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.Surface;
 
 /**
  * This class represents a request to an {@link RotationResolverService}. The request contains
@@ -54,7 +55,7 @@
         mTimeoutMillis = timeoutMillis;
     }
 
-    public int getProposedRotation() {
+    @Surface.Rotation public int getProposedRotation() {
         return mProposedRotation;
     }
 
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 593a642..604dd0a 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -146,11 +146,8 @@
         }
         mPendingCallback = new RotationResolverCallbackWrapper(callback, this);
         mCancellationSignal = CancellationSignal.fromTransport(transport);
-        try {
-            onResolveRotation(request, mCancellationSignal, mPendingCallback);
-        } catch (UnsupportedOperationException e) {
-            reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED);
-        }
+
+        onResolveRotation(request, mCancellationSignal, mPendingCallback);
     }
 
     @MainThread
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
      * @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
      * @param bytes number of bytes which need to be freed
      */
-    public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+    public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
         throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
     }
 
@@ -202,7 +202,7 @@
                 RemoteCallback callback) {
             mHandler.post(() -> {
                 try {
-                    onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+                    onFreeCache(StorageManager.convert(volumeUuid), bytes);
                     sendResult(sessionId, null /* throwable */, callback);
                 } catch (Throwable t) {
                     sendResult(sessionId, t, callback);
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index f7710e6..ff03cc1 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
@@ -44,6 +45,7 @@
     private ServiceInfo mServiceInfo;
     private String mSessionService;
     private String mRecognitionService;
+    private String mHotwordDetectionService;
     private String mSettingsActivity;
     private boolean mSupportsAssist;
     private boolean mSupportsLaunchFromKeyguard;
@@ -133,6 +135,8 @@
                     false);
             mSupportsLocalInteraction = array.getBoolean(com.android.internal.
                     R.styleable.VoiceInteractionService_supportsLocalInteraction, false);
+            mHotwordDetectionService = array.getString(com.android.internal.R.styleable
+                    .VoiceInteractionService_hotwordDetectionService);
             array.recycle();
             if (mSessionService == null) {
                 mParseError = "No sessionService specified";
@@ -181,4 +185,9 @@
     public boolean getSupportsLocalInteraction() {
         return mSupportsLocalInteraction;
     }
+
+    @Nullable
+    public String getHotwordDetectionService() {
+        return mHotwordDetectionService;
+    }
 }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 16b45c3..7f45b38 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -475,9 +475,8 @@
 
         mObjects.insertAt(0, dirs);
 
-        final int baseLength = mBase.length();
-        // Update from 0 characters to whatever the real text is
-        reflow(mBase, 0, 0, baseLength);
+        // Update from 0 characters to whatever the displayed text is
+        reflow(mBase, 0, 0, mDisplay.length());
 
         if (mBase instanceof Spannable) {
             if (mWatcher == null)
@@ -485,6 +484,7 @@
 
             // Strip out any watchers for other DynamicLayouts.
             final Spannable sp = (Spannable) mBase;
+            final int baseLength = mBase.length();
             final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class);
             for (int i = 0; i < spans.length; i++) {
                 sp.removeSpan(spans[i]);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 2de7558..aef185c 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -36,6 +36,7 @@
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 
 /**
@@ -155,6 +156,32 @@
         }
     };
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        FontConfig that = (FontConfig) o;
+        return mLastModifiedTimeMillis == that.mLastModifiedTimeMillis
+                && mConfigVersion == that.mConfigVersion
+                && Objects.equals(mFamilies, that.mFamilies)
+                && Objects.equals(mAliases, that.mAliases);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFamilies, mAliases, mLastModifiedTimeMillis, mConfigVersion);
+    }
+
+    @Override
+    public String toString() {
+        return "FontConfig{"
+                + "mFamilies=" + mFamilies
+                + ", mAliases=" + mAliases
+                + ", mLastModifiedTimeMillis=" + mLastModifiedTimeMillis
+                + ", mConfigVersion=" + mConfigVersion
+                + '}';
+    }
+
     /**
      * Represents single font entry in system font configuration.
      *
@@ -317,6 +344,37 @@
         public boolean isItalic() {
             return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Font font = (Font) o;
+            return mIndex == font.mIndex
+                    && Objects.equals(mFile, font.mFile)
+                    && Objects.equals(mOriginalFile, font.mOriginalFile)
+                    && Objects.equals(mStyle, font.mStyle)
+                    && Objects.equals(mFontVariationSettings, font.mFontVariationSettings)
+                    && Objects.equals(mFontFamilyName, font.mFontFamilyName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFile, mOriginalFile, mStyle, mIndex, mFontVariationSettings,
+                    mFontFamilyName);
+        }
+
+        @Override
+        public String toString() {
+            return "Font{"
+                    + "mFile=" + mFile
+                    + ", mOriginalFile=" + mOriginalFile
+                    + ", mStyle=" + mStyle
+                    + ", mIndex=" + mIndex
+                    + ", mFontVariationSettings='" + mFontVariationSettings + '\''
+                    + ", mFontFamilyName='" + mFontFamilyName + '\''
+                    + '}';
+        }
     }
 
     /**
@@ -398,6 +456,30 @@
                 return new Alias[size];
             }
         };
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Alias alias = (Alias) o;
+            return mWeight == alias.mWeight
+                    && Objects.equals(mName, alias.mName)
+                    && Objects.equals(mOriginal, alias.mOriginal);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mName, mOriginal, mWeight);
+        }
+
+        @Override
+        public String toString() {
+            return "Alias{"
+                    + "mName='" + mName + '\''
+                    + ", mOriginal='" + mOriginal + '\''
+                    + ", mWeight=" + mWeight
+                    + '}';
+        }
     }
 
     /**
@@ -413,7 +495,7 @@
     public static final class FontFamily implements Parcelable {
         private final @NonNull List<Font> mFonts;
         private final @Nullable String mName;
-        private final @Nullable LocaleList mLocaleList;
+        private final @NonNull LocaleList mLocaleList;
         private final @Variant int mVariant;
 
         /** @hide */
@@ -454,7 +536,7 @@
          * @hide Only system server can create this instance and passed via IPC.
          */
         public FontFamily(@NonNull List<Font> fonts, @Nullable String name,
-                @Nullable LocaleList localeList, @Variant int variant) {
+                @NonNull LocaleList localeList, @Variant int variant) {
             mFonts = fonts;
             mName = name;
             mLocaleList = localeList;
@@ -493,8 +575,6 @@
          *
          * The locale list will be used for deciding which font family should be used in fallback
          * list.
-         *
-         * @return non-null if a locale list is available. Otherwise null.
          */
         public @NonNull LocaleList getLocaleList() {
             return mLocaleList;
@@ -559,5 +639,31 @@
         public @NonNull String getLanguages() {
             return mLocaleList.toLanguageTags();
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            FontFamily that = (FontFamily) o;
+            return mVariant == that.mVariant
+                    && Objects.equals(mFonts, that.mFonts)
+                    && Objects.equals(mName, that.mName)
+                    && Objects.equals(mLocaleList, that.mLocaleList);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFonts, mName, mLocaleList, mVariant);
+        }
+
+        @Override
+        public String toString() {
+            return "FontFamily{"
+                    + "mFonts=" + mFonts
+                    + ", mName='" + mName + '\''
+                    + ", mLocaleList=" + mLocaleList
+                    + ", mVariant=" + mVariant
+                    + '}';
+        }
     }
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 85911ff..f99d430 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -977,6 +977,9 @@
                 calculateEllipsis(start, end, measured, widthStart,
                         ellipsisWidth, ellipsize, j,
                         textWidth, paint, forceEllipsis);
+            } else {
+                mLines[mColumns * j + ELLIPSIS_START] = 0;
+                mLines[mColumns * j + ELLIPSIS_COUNT] = 0;
             }
         }
 
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
new file mode 100644
index 0000000..4520db3
--- /dev/null
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+/**
+ * Binder interface for the TracingServiceProxy running in system_server.
+ *
+ * {@hide}
+ */
+interface ITracingServiceProxy
+{
+    /**
+     * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+     * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+     * there is no buffer available to dump.
+     */
+    oneway void notifyTraceSessionEnded(boolean sessionStolen);
+}
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/core/java/android/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773f..b229212 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
-        DEFAULT_FLAGS.put("settings_silky_home", "true");
+        DEFAULT_FLAGS.put("settings_silky_home", "false");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
     }
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d4..c0d8187 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
                 Log.w(TAG,
-                        "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+                        "onRangingOpenedFailed - received unexpected SessionHandle: "
+                                + sessionHandle);
                 return;
             }
 
@@ -124,7 +125,7 @@
             @RangingChangeReason int reason, PersistableBundle params) {
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
-                Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+                Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
                         + sessionHandle);
                 return;
             }
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
index 928fcbdc..b23f5ad 100644
--- a/core/java/android/uwb/SessionHandle.java
+++ b/core/java/android/uwb/SessionHandle.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * @hide
  */
@@ -73,6 +75,11 @@
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hashCode(mId);
+    }
+
+    @Override
     public String toString() {
         return "SessionHandle [id=" + mId + "]";
     }
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4168064..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -59,8 +59,12 @@
  * an application window, excluding the system decorations.  The application display area may
  * be smaller than the real display area because the system subtracts the space needed
  * for decor elements such as the status bar.  Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations.  Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size).  Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
  * </ul>
  * </p><p>
  * A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
     @UnsupportedAppUsage
     public DisplayAdjustments getDisplayAdjustments() {
         if (mResources != null) {
-            final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
-            if (!mDisplayAdjustments.equals(currentAdjustments)) {
-                mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+            final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+            if (!mDisplayAdjustments.equals(currentAdjustements)) {
+                mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
             }
         }
 
@@ -1213,34 +1217,30 @@
     }
 
     /**
-     * Provides the largest {@link Point outSize} an app may expect in the current system state,
-     * without subtracting any window decor.
+     * Gets the real size of the display without subtracting any window decor or
+     * applying any compatibility scale factors.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
-     * </p>
+     * </p><p>
+     * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+     * report the same bounds except that certain areas of the display may not be available to
+     * windows created in the {@link WindowManager}'s {@link Context}.
+     *
+     * For example, imagine a device which has a multi-task mode that limits windows to half of the
+     * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+     * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+     * still reports the bounds of the whole display.
      *
      * @param outSize Set to the real size of the display.
+     *
+     * @see WindowManager#getMaximumWindowMetrics()
      */
     public void getRealSize(Point outSize) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                final Rect bounds = mResources.getConfiguration()
-                        .windowConfiguration.getMaxBounds();
-                outSize.x = bounds.width();
-                outSize.y = bounds.height();
-                if (DEBUG) {
-                    Log.d(TAG, "getRealSize determined from max bounds: " + outSize
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             outSize.x = mDisplayInfo.logicalWidth;
             outSize.y = mDisplayInfo.logicalHeight;
             if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@
     }
 
     /**
-     * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
-     * system state, without subtracting any window decor.
+     * Gets display metrics based on the real size of this display.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@
     public void getRealMetrics(DisplayMetrics outMetrics) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                mDisplayInfo.getMaxBoundsMetrics(outMetrics,
-                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
-                        mResources.getConfiguration());
-                if (DEBUG) {
-                    Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             mDisplayInfo.getLogicalMetrics(outMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
             if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@
     }
 
     /**
-     * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
-     * display dimensions. The max bounds field may be smaller than the logical dimensions
-     * when apps need to be sandboxed.
-     * @return {@code true} when max bounds should be applied.
-     */
-    private boolean shouldReportMaxBounds() {
-        if (mResources == null) {
-            return false;
-        }
-        final Configuration config = mResources.getConfiguration();
-        return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
-    }
-
-    /**
      * Gets the state of the display, such as whether it is on or off.
      *
      * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5d4a4e5..e6cd252 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -58,11 +58,11 @@
     public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
 
     /**
-     * Specifies to generate config changed events from Surface Flinger.
+     * Specifies to generate mode changed events from Surface Flinger.
      * <p>
      * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
      */
-    public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1;
+    public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1;
 
     /**
      * Specifies to generate frame rate override events from Surface Flinger.
@@ -197,14 +197,14 @@
     }
 
     /**
-     * Called when a display config changed event is received.
+     * Called when a display mode changed event is received.
      *
      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
      * timebase.
      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
-     * @param configId The new config Id
+     * @param modeId The new mode Id
      */
-    public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+    public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
     }
 
     /**
@@ -273,8 +273,8 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
-        onConfigChanged(timestampNanos, physicalDisplayId, configId);
+    private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+        onModeChanged(timestampNanos, physicalDisplayId, modeId);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
 import static android.view.DisplayInfoProto.NAME;
 
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -616,29 +615,11 @@
         getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
     }
 
-    /**
-     * Populates {@code outMetrics} with details of the logical display. Bounds are limited
-     * by the logical size of the display.
-     *
-     * @param outMetrics the {@link DisplayMetrics} to be populated
-     * @param compatInfo the {@link CompatibilityInfo} to be applied
-     * @param configuration the {@link Configuration}
-     */
     public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
             Configuration configuration) {
         getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
     }
 
-    /**
-     * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
-     * {@link WindowConfiguration#getMaxBounds()}
-     */
-    public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
-            Configuration configuration) {
-        Rect bounds = configuration.windowConfiguration.getMaxBounds();
-        getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
-    }
-
     public int getNaturalWidth() {
         return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
                 logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
     /**
      * Called when the process needs to start the remote animation.
      *
+     * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
      * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
      * @param finishedCallback The callback to invoke when the animation is finished.
      */
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+    void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+            in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
             in IRemoteAnimationFinishedCallback finishedCallback);
 
     /**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ae8afca..62f4b86 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -124,16 +124,10 @@
      *
      * @param token Token to be registered.
      * @param type Window type to be used with this token.
-     * @param options A bundle used to pass window-related options.
      * @param displayId The ID of the display where this token should be added.
-     * @param packageName The name of package to request to add window token. Could be {@code null}
-     *                    if callers holds the MANAGE_APP_TOKENS permission.
-     * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
-     *         otherwise.
+     * @param options A bundle used to pass window-related options.
      */
-    int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
-            String packageName);
-    void addWindowToken(IBinder token, int type, int displayId);
+    void addWindowToken(IBinder token, int type, int displayId, in Bundle options);
     /**
      * Remove window token on a specific display.
      *
@@ -784,6 +778,10 @@
     /**
      * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
      * from the server side.
+     * <p>
+     * Note that this API should be invoked after calling
+     * {@link android.app.WindowTokenClient#attachContext(WindowContext)}
+     * </p>
      *
      * @param clientToken the window context's token
      * @param type Window type of the window context
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 990b7bd..dad932c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,14 +123,6 @@
     boolean outOfMemory(IWindow window);
 
     /**
-     * Give the window manager a hint of the part of the window that is
-     * completely transparent, allowing it to work with the surface flinger
-     * to optimize compositing of this part of the window.
-     */
-    @UnsupportedAppUsage
-    oneway void setTransparentRegion(IWindow window, in Region region);
-
-    /**
      * Tell the window manager about the content and visible insets of the
      * given window, which can be used to adjust the <var>outContentInsets</var>
      * and <var>outVisibleInsets</var> values returned by
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
                 types, mCallbacks, durationMs, interpolator, animationType, translator);
         InsetsAnimationThread.getHandler().post(() -> {
+            if (mControl.isCancelled()) {
+                return;
+            }
             Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
                     "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
             listener.onReady(mControl, types);
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 24bc308..f8c4d15 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -928,7 +928,9 @@
      * seamless transition is one that doesn't have any visual interruptions, such as a black
      * screen for a second or two. True indicates that any frame rate changes caused by this
      * request should be seamless. False indicates that non-seamless refresh rates are also
-     * acceptable.
+     * acceptable. Non-seamless switches might be used when the benefit of matching the content's
+     * frame rate outweighs the cost of the transition, for example when displaying
+     * long-running video content.
      *
      * @throws IllegalArgumentException If frameRate or compatibility are invalid.
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index acd2507..6a629ca 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,8 +161,8 @@
             int L, int T, int R, int B);
     private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
             int width, int height);
-    private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
-    private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs(
+    private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
+    private static native DisplayMode[] nativeGetDisplayModes(
             IBinder displayToken);
     private static native DisplayedContentSamplingAttributes
             nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
@@ -170,13 +170,13 @@
             boolean enable, int componentMask, int maxFrames);
     private static native DisplayedContentSample nativeGetDisplayedContentSample(
             IBinder displayToken, long numFrames, long timestamp);
-    private static native int nativeGetActiveConfig(IBinder displayToken);
-    private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
-            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
-    private static native SurfaceControl.DesiredDisplayConfigSpecs
-            nativeGetDesiredDisplayConfigSpecs(IBinder displayToken);
+    private static native int nativeGetActiveDisplayMode(IBinder displayToken);
+    private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken,
+            DesiredDisplayModeSpecs desiredDisplayModeSpecs);
+    private static native DesiredDisplayModeSpecs
+            nativeGetDesiredDisplayModeSpecs(IBinder displayToken);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
-    private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+    private static native DisplayPrimaries nativeGetDisplayNativePrimaries(
             IBinder displayToken);
     private static native int[] nativeGetCompositionDataspaces();
     private static native int nativeGetActiveColorMode(IBinder displayToken);
@@ -188,8 +188,6 @@
             IBinder displayToken, int mode);
     private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
             long barrierObject, long frame);
-    private static native void nativeReparentChildren(long transactionObj, long nativeObject,
-            long newParentObject);
     private static native void nativeReparent(long transactionObj, long nativeObject,
             long newParentNativeObject);
 
@@ -1745,11 +1743,11 @@
      *
      * @hide
      */
-    public static final class DisplayConfig {
+    public static final class DisplayMode {
         /**
          * Invalid display config id.
          */
-        public static final int INVALID_DISPLAY_CONFIG_ID = -1;
+        public static final int INVALID_DISPLAY_MODE_ID = -1;
 
         public int width;
         public int height;
@@ -1766,7 +1764,7 @@
          * configs within the same group can be done seamlessly in most cases.
          * @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP
          */
-        public int configGroup;
+        public int group;
 
         @Override
         public String toString() {
@@ -1777,7 +1775,7 @@
                     + ", refreshRate=" + refreshRate
                     + ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
                     + ", presentationDeadlineNanos=" + presentationDeadlineNanos
-                    + ", configGroup=" + configGroup + "}";
+                    + ", group=" + group + "}";
         }
     }
 
@@ -1804,21 +1802,21 @@
     /**
      * @hide
      */
-    public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) {
+    public static DisplayMode[] getDisplayModes(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        return nativeGetDisplayConfigs(displayToken);
+        return nativeGetDisplayModes(displayToken);
     }
 
     /**
      * @hide
      */
-    public static int getActiveConfig(IBinder displayToken) {
+    public static int getActiveDisplayMode(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        return nativeGetActiveConfig(displayToken);
+        return nativeGetActiveDisplayMode(displayToken);
     }
 
     /**
@@ -1865,8 +1863,8 @@
      *
      * @hide
      */
-    public static final class DesiredDisplayConfigSpecs {
-        public int defaultConfig;
+    public static final class DesiredDisplayModeSpecs {
+        public int defaultMode;
         /**
          * The primary refresh rate range represents display manager's general guidance on the
          * display configs surface flinger will consider when switching refresh rates. Unless
@@ -1891,16 +1889,16 @@
          */
         public boolean allowGroupSwitching;
 
-        public DesiredDisplayConfigSpecs() {}
+        public DesiredDisplayModeSpecs() {}
 
-        public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) {
+        public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) {
             copyFrom(other);
         }
 
-        public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching,
+        public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching,
                 float primaryRefreshRateMin, float primaryRefreshRateMax,
                 float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
-            this.defaultConfig = defaultConfig;
+            this.defaultMode = defaultMode;
             this.allowGroupSwitching = allowGroupSwitching;
             this.primaryRefreshRateMin = primaryRefreshRateMin;
             this.primaryRefreshRateMax = primaryRefreshRateMax;
@@ -1910,14 +1908,14 @@
 
         @Override
         public boolean equals(@Nullable Object o) {
-            return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
+            return o instanceof DesiredDisplayModeSpecs && equals((DesiredDisplayModeSpecs) o);
         }
 
         /**
          * Tests for equality.
          */
-        public boolean equals(DesiredDisplayConfigSpecs other) {
-            return other != null && defaultConfig == other.defaultConfig
+        public boolean equals(DesiredDisplayModeSpecs other) {
+            return other != null && defaultMode == other.defaultMode
                     && primaryRefreshRateMin == other.primaryRefreshRateMin
                     && primaryRefreshRateMax == other.primaryRefreshRateMax
                     && appRequestRefreshRateMin == other.appRequestRefreshRateMin
@@ -1932,8 +1930,8 @@
         /**
          * Copies the supplied object's values to this object.
          */
-        public void copyFrom(DesiredDisplayConfigSpecs other) {
-            defaultConfig = other.defaultConfig;
+        public void copyFrom(DesiredDisplayModeSpecs other) {
+            defaultMode = other.defaultMode;
             primaryRefreshRateMin = other.primaryRefreshRateMin;
             primaryRefreshRateMax = other.primaryRefreshRateMax;
             appRequestRefreshRateMin = other.appRequestRefreshRateMin;
@@ -1944,7 +1942,7 @@
         public String toString() {
             return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
                             + " appRequestRefreshRateRange=[%.0f %.0f]",
-                    defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax,
+                    defaultMode, primaryRefreshRateMin, primaryRefreshRateMax,
                     appRequestRefreshRateMin, appRequestRefreshRateMax);
         }
     }
@@ -1952,25 +1950,31 @@
     /**
      * @hide
      */
-    public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
-            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
+    public static boolean setDesiredDisplayModeSpecs(IBinder displayToken,
+            DesiredDisplayModeSpecs desiredDisplayModeSpecs) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
+        if (desiredDisplayModeSpecs == null) {
+            throw new IllegalArgumentException("desiredDisplayModeSpecs must not be null");
+        }
+        if (desiredDisplayModeSpecs.defaultMode < 0) {
+            throw new IllegalArgumentException("defaultMode must be non-negative");
+        }
 
-        return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
+        return nativeSetDesiredDisplayModeSpecs(displayToken, desiredDisplayModeSpecs);
     }
 
     /**
      * @hide
      */
-    public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(
+    public static DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(
             IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
 
-        return nativeGetDesiredDisplayConfigSpecs(displayToken);
+        return nativeGetDesiredDisplayModeSpecs(displayToken);
     }
 
     /**
@@ -2041,7 +2045,7 @@
     /**
      * @hide
      */
-    public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+    public static DisplayPrimaries getDisplayNativePrimaries(
             IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -2970,15 +2974,6 @@
         }
 
         /**
-         * @hide
-         */
-        public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
-            checkPreconditions(sc);
-            nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
-            return this;
-        }
-
-        /**
          * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
          * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
          * parent Surface.
@@ -3252,7 +3247,10 @@
          *                         interruptions, such as a black screen for a second or two. True
          *                         indicates that any frame rate changes caused by this request
          *                         should be seamless. False indicates that non-seamless refresh
-         *                         rates are also acceptable.
+         *                         rates are also acceptable. Non-seamless switches might be
+         *                         used when the benefit of matching the content's frame rate
+         *                         outweighs the cost of the transition, for example when
+         *                         displaying long-running video content.
          * @return This transaction object.
          */
         @NonNull
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f019648..18029af 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -162,7 +162,6 @@
             @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
-        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
@@ -188,7 +187,6 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
-        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index b10370a..acbcbfa 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,12 +85,13 @@
         for (int i = params.length - 1; i >= 0; i--) {
             SurfaceParams surfaceParams = params[i];
             SurfaceControl surface = surfaceParams.surface;
-            if (frame > 0) {
-                t.deferTransactionUntil(surface, mTargetSc, frame);
-            }
             applyParams(t, surfaceParams, mTmpFloat9);
         }
-        t.apply();
+        if (mTargetViewRootImpl != null) {
+            mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+        } else {
+            t.apply();
+        }
     }
 
     public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 749c0df..1273b49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -740,6 +740,7 @@
  * @attr ref android.R.styleable#View_alpha
  * @attr ref android.R.styleable#View_background
  * @attr ref android.R.styleable#View_clickable
+ * @attr ref android.R.styleable#View_clipToOutline
  * @attr ref android.R.styleable#View_contentDescription
  * @attr ref android.R.styleable#View_drawingCacheQuality
  * @attr ref android.R.styleable#View_duplicateParentState
@@ -5968,6 +5969,9 @@
                 case R.styleable.View_scrollCaptureHint:
                     setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO)));
                     break;
+                case R.styleable.View_clipToOutline:
+                    setClipToOutline(a.getBoolean(attr, false));
+                    break;
             }
         }
 
@@ -8740,14 +8744,17 @@
     /**
      * Populates a {@link ViewStructure} for content capture.
      *
-     * <p>This method is called after a view is that is eligible for content capture
-     * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
-     * the user, and the activity rendering the view is enabled for content capture) is laid out and
-     * is visible.
-     *
-     * <p>The populated structure is then passed to the service through
+     * <p>This method is called after a view that is eligible for content capture
+     * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is
+     * enabled for the user, and the activity rendering the view is enabled for content capture)
+     * is laid out and is visible. The populated structure is then passed to the service through
      * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
      *
+     * <p>The default implementation of this method sets the most relevant properties based on
+     * related {@link View} methods, and views in the standard Android widgets library also
+     * override it to set their relevant properties. Therefore, if overriding this method, it
+     * is recommended to call {@code super.onProvideContentCaptureStructure()}.
+     *
      * <p><b>Note: </b>views that manage a virtual structure under this view must populate just
      * the node representing this view and return right away, then asynchronously report (not
      * necessarily in the UI thread) when the children nodes appear, disappear or have their text
@@ -8755,7 +8762,7 @@
      * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
      * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
      * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
-     * respectively. The structure for the a child must be created using
+     * respectively. The structure for a child must be created using
      * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
      * {@code autofillId} for a child can be obtained either through
      * {@code childStructure.getAutofillId()} or
@@ -8900,7 +8907,7 @@
     /**
      * Called when assist structure is being retrieved from a view as part of
      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
-     * generate additional virtual structure under this view.  The defaullt implementation
+     * generate additional virtual structure under this view.  The default implementation
      * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
      * view's virtual accessibility nodes, if any.  You can override this for a more
      * optimal implementation providing this data.
@@ -17918,6 +17925,8 @@
      *
      * @see #setOutlineProvider(ViewOutlineProvider)
      * @see #getClipToOutline()
+     *
+     * @attr ref android.R.styleable#View_clipToOutline
      */
     @RemotableViewMethod
     public void setClipToOutline(boolean clipToOutline) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 18ef80c..4716141 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,7 +315,7 @@
      * In that case we receive a call back from {@link ActivityThread} and this flag is used to
      * preserve the initial value.
      *
-     * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+     * @see #performConfigurationChange(MergedConfiguration, boolean, int)
      */
     private boolean mForceNextConfigUpdate;
 
@@ -1675,7 +1675,8 @@
         requestLayout();
 
         // See comment for View.sForceLayoutWhenInsetsChanged
-        if (View.sForceLayoutWhenInsetsChanged && mView != null) {
+        if (View.sForceLayoutWhenInsetsChanged && mView != null
+                && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) {
             forceLayout(mView);
         }
 
@@ -3063,11 +3064,14 @@
                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                     mPreviousTransparentRegion.set(mTransparentRegion);
                     mFullRedrawNeeded = true;
-                    // reconfigure window manager
-                    try {
-                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
-                    } catch (RemoteException e) {
-                    }
+                    // TODO: Ideally we would do this in prepareSurfaces,
+                    // but prepareSurfaces is currently working under
+                    // the assumption that we paused the render thread
+                    // via the WM relayout code path. We probably eventually
+                    // want to synchronize transparent region hint changes
+                    // with draws.
+                    mTransaction.setTransparentRegionHint(getSurfaceControl(),
+                        mTransparentRegion).apply();
                 }
             }
 
@@ -10137,9 +10141,11 @@
      * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
      * you can add transactions to the upcoming frame.
      */
-    void mergeWithNextTransaction(Transaction t, long frameNumber) {
+    public void mergeWithNextTransaction(Transaction t, long frameNumber) {
         if (mBlastBufferQueue != null) {
             mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
+        } else {
+            t.apply();
         }
     }
 }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index dd0ab65..47ac1ee 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -116,14 +116,6 @@
      */
     public static final int RELAYOUT_INSETS_PENDING = 0x1;
 
-    /**
-     * Flag for relayout: the client may be currently using the current surface,
-     * so if it is to be destroyed as a part of the relayout the destroy must
-     * be deferred until later.  The client will call performDeferredDestroy()
-     * when it is okay.
-     */
-    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
-
     public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
     public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
@@ -147,7 +139,6 @@
     public static final int ADD_INVALID_DISPLAY = -9;
     public static final int ADD_INVALID_TYPE = -10;
     public static final int ADD_INVALID_USER = -11;
-    public static final int ADD_TOO_MANY_TOKENS = -12;
 
     @UnsupportedAppUsage
     private static WindowManagerGlobal sDefaultWindowManager;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b85f1079..39d3c01 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -71,7 +71,7 @@
         new HashMap<IBinder, ResizeCompleteCallback>();
 
     private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final SurfaceControl mRootSurface;
+    protected final SurfaceControl mRootSurface;
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
     private final IBinder mHostInputToken;
@@ -126,7 +126,7 @@
         }
     }
 
-    protected void attachToParentSurface(SurfaceControl.Builder b) {
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
         b.setParent(mRootSurface);
     }
 
@@ -140,10 +140,10 @@
             InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
                 .setFormat(attrs.format)
-                .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
+                .setBLASTLayer()
                 .setName(attrs.getTitle().toString())
                 .setCallsite("WindowlessWindowManager.addToDisplay");
-        attachToParentSurface(b);
+        attachToParentSurface(window, b);
         final SurfaceControl sc = b.build();
 
         if (((attrs.inputFeatures &
@@ -162,7 +162,11 @@
             mStateForWindow.put(window.asBinder(), state);
         }
 
-        return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+        final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
+                        WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+
+        // Include whether the window is in touch mode.
+        return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
     }
 
     /**
@@ -210,6 +214,26 @@
         return !PixelFormat.formatHasAlpha(attrs.format);
     }
 
+    private boolean isInTouchMode() {
+        try {
+            return WindowManagerGlobal.getWindowSession().getInTouchMode();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to check if the window is in touch mode", e);
+        }
+        return false;
+    }
+
+    /** Access to package members for SystemWindow leashing
+     * @hide
+     */
+    protected IBinder getWindowBinder(View rootView) {
+        final ViewRootImpl root = rootView.getViewRootImpl();
+        if (root == null) {
+            return null;
+        }
+        return root.mWindow.asBinder();
+    }
+
     /** @hide */
     @Nullable
     protected SurfaceControl getSurfaceControl(View rootView) {
@@ -254,8 +278,8 @@
         WindowManager.LayoutParams attrs = state.mParams;
 
         if (viewFlags == View.VISIBLE) {
-            t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
-                    .setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+            outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
+            t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
             outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
         } else {
             t.hide(sc).apply();
@@ -276,7 +300,8 @@
             }
         }
 
-        return 0;
+        // Include whether the window is in touch mode.
+        return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
     }
 
     @Override
@@ -289,10 +314,6 @@
     }
 
     @Override
-    public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) {
-    }
-
-    @Override
     public void setInsets(android.view.IWindow window, int touchableInsets,
             android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
             android.graphics.Region touchableRegion) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 794181e..decbf8c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -200,6 +200,34 @@
             "android.view.autofill.extra.AUTHENTICATION_RESULT";
 
     /**
+     * Intent extra: The optional boolean extra field provided by the
+     * {@link android.service.autofill.AutofillService} accompanying the {@link
+     * android.service.autofill.Dataset} result of an authentication operation.
+     *
+     * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a
+     * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also
+     * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}.
+     * That means if the user clears the field values, the autofill suggestion will show up again
+     * with the new authenticated Dataset.
+     *
+     * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior
+     * that if the Dataset being authenticated is a pinned dataset (see
+     * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be
+     * replaced.
+     *
+     * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to
+     * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether
+     * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to
+     * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the
+     * returned Dataset will not replace the old dataset from the existing
+     * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not
+     * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is
+     * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}.
+     */
+    public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET =
+            "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
+
+    /**
      * Intent extra: The optional extras provided by the
      * {@link android.service.autofill.AutofillService}.
      *
@@ -1755,6 +1783,11 @@
             if (newClientState != null) {
                 responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
             }
+            if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+                responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+                        data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+                                false));
+            }
             try {
                 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
                         mContext.getUserId());
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 415b3a7..37220fe 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -164,12 +164,12 @@
 
     /**
      * Default implementation calls {@link #finishComposingText()} and
-     * {@code setImeTemporarilyConsumesInput(false)}.
+     * {@code setImeConsumesInput(false)}.
      */
     @CallSuper
     public void closeConnection() {
         finishComposingText();
-        setImeTemporarilyConsumesInput(false);
+        setImeConsumesInput(false);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2df75f6..bde4cb7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -299,7 +299,7 @@
      * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode.
      * @hide
      */
-    public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000;
+    public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001;
 
     /**
      * Generic unspecified type for {@link #imeOptions}.
@@ -321,7 +321,6 @@
      *                               1 1 IME_ACTION_NEXT
      *                               11  IME_ACTION_DONE
      *                               111 IME_ACTION_PREVIOUS
-     *          1                        IME_FLAG_APP_WINDOW_PORTRAIT
      *         1                         IME_FLAG_NO_PERSONALIZED_LEARNING
      *        1                          IME_FLAG_NO_FULLSCREEN
      *       1                           IME_FLAG_NAVIGATE_PREVIOUS
@@ -356,7 +355,7 @@
      * Masks for {@link internalImeOptions}
      *
      * <pre>
-     *  1                                IME_FLAG_APP_WINDOW_PORTRAIT
+     *                                 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT
      * |-------|-------|-------|-------|</pre>
      */
 
@@ -984,6 +983,7 @@
         dest.writeInt(inputType);
         dest.writeInt(imeOptions);
         dest.writeString(privateImeOptions);
+        dest.writeInt(internalImeOptions);
         TextUtils.writeToParcel(actionLabel, dest, flags);
         dest.writeInt(actionId);
         dest.writeInt(initialSelStart);
@@ -1019,6 +1019,7 @@
                     res.inputType = source.readInt();
                     res.imeOptions = source.readInt();
                     res.privateImeOptions = source.readString();
+                    res.internalImeOptions = source.readInt();
                     res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
                     res.actionId = source.readInt();
                     res.initialSelStart = source.readInt();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 6300320..0ab4e05 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.LocaleList;
@@ -69,7 +73,8 @@
 
     /**
      * The IME provided locales for the request. If non-empty, the inline suggestions should
-     * return languages from the supported locales. If not provided, it'll default to system locale.
+     * return languages from the supported locales. If not provided, it'll default to be empty if
+     * target SDK is S or above, and default to system locale otherwise.
      *
      * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
      * to have one locale to guarantee consistent UI rendering.</p>
@@ -156,7 +161,18 @@
         return ActivityThread.currentPackageName();
     }
 
+    /**
+     * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when
+     * it's not set, instead of the default system locale.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY = 169273070L;
+
     private static LocaleList defaultSupportedLocales() {
+        if (CompatChanges.isChangeEnabled(IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY)) {
+            return LocaleList.getEmptyLocaleList();
+        }
         return LocaleList.getDefault();
     }
 
@@ -189,7 +205,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -264,7 +280,8 @@
 
     /**
      * The IME provided locales for the request. If non-empty, the inline suggestions should
-     * return languages from the supported locales. If not provided, it'll default to system locale.
+     * return languages from the supported locales. If not provided, it'll default to be empty if
+     * target SDK is S or above, and default to system locale otherwise.
      *
      * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
      * to have one locale to guarantee consistent UI rendering.</p>
@@ -522,7 +539,8 @@
 
         /**
          * The IME provided locales for the request. If non-empty, the inline suggestions should
-         * return languages from the supported locales. If not provided, it'll default to system locale.
+         * return languages from the supported locales. If not provided, it'll default to be empty if
+         * target SDK is S or above, and default to system locale otherwise.
          *
          * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
          * to have one locale to guarantee consistent UI rendering.</p>
@@ -622,10 +640,10 @@
     }
 
     @DataClass.Generated(
-            time = 1595457701315L,
-            codegenVersion = "1.0.15",
+            time = 1612206506050L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index f3111bd..34a60bb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1004,20 +1004,19 @@
             @Nullable Bundle opts);
 
     /**
-     * Called by the input method to indicate that it temporarily consumes all input for itself,
-     * or no longer does so.
+     * Called by the input method to indicate that it consumes all input for itself, or no longer
+     * does so.
      *
-     * <p>Editors should reflect that they are temporarily not receiving input by hiding the
-     * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the
-     * cursor if it is {@code false}.
+     * <p>Editors should reflect that they are not receiving input by hiding the cursor if
+     * {@code imeConsumesInput} is {@code true}, and resume showing the cursor if it is
+     * {@code false}.
      *
-     * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input
-     * and the cursor should be hidden, {@code false} when input to the editor resumes and the
-     * cursor should be shown again.
+     * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
+     * hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
      * @return {@code true} on success, {@code false} if the input connection is no longer valid, or
      * the protocol is not supported.
      */
-    default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+    default boolean setImeConsumesInput(boolean imeConsumesInput) {
         return false;
     }
 }
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index b29149f..b1501a4 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -341,7 +341,7 @@
      * @throws NullPointerException if the target is {@code null}.
      */
     @Override
-    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
-        return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
+        return mTarget.setImeConsumesInput(imeConsumesInput);
     }
 }
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 046f75f..2452e4c 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
 
 /**
  * <p>
@@ -52,6 +53,7 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
+@RemoteView
 public class CheckBox extends CompoundButton {
     public CheckBox(Context context) {
         this(context, null);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 135ff9f..63f8ee7 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -27,11 +27,13 @@
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.RemotableViewMethod;
 import android.view.SoundEffectConstants;
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
@@ -275,6 +277,7 @@
      * @param resId the resource identifier of the drawable
      * @attr ref android.R.styleable#CompoundButton_button
      */
+    @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync")
     public void setButtonDrawable(@DrawableRes int resId) {
         final Drawable d;
         if (resId != 0) {
@@ -285,6 +288,12 @@
         setButtonDrawable(d);
     }
 
+    /** @hide **/
+    public Runnable setButtonDrawableAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setButtonDrawable(drawable);
+    }
+
     /**
      * Sets a drawable as the compound button image.
      *
@@ -336,6 +345,23 @@
     }
 
     /**
+     * Sets the button of this CompoundButton to the specified Icon.
+     *
+     * @param icon an Icon holding the desired button, or {@code null} to clear
+     *             the button
+     */
+    @RemotableViewMethod(asyncImpl = "setButtonIconAsync")
+    public void setButtonIcon(@Nullable Icon icon) {
+        setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setButtonIconAsync(@Nullable Icon icon) {
+        Drawable button = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setButtonDrawable(button);
+    }
+
+    /**
      * Applies a tint to the button drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -350,6 +376,7 @@
      * @see #setButtonTintList(ColorStateList)
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setButtonTintList(@Nullable ColorStateList tint) {
         mButtonTintList = tint;
         mHasButtonTint = true;
@@ -394,6 +421,7 @@
      * @see #getButtonTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setButtonTintBlendMode(@Nullable BlendMode tintMode) {
         mButtonBlendMode = tintMode;
         mHasButtonBlendMode = true;
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index a04d7c3..9b35034 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -49,6 +50,7 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
+@RemoteView
 public class RadioButton extends CompoundButton {
 
     public RadioButton(Context context) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 4722fdc..d445fdc 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -31,6 +31,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -59,6 +60,7 @@
  * @see RadioButton
  *
  */
+@RemoteView
 public class RadioGroup extends LinearLayout {
     private static final String LOG_TAG = RadioGroup.class.getSimpleName();
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b47a0ac..6cb4b81 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -47,6 +47,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Outline;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -79,6 +80,7 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewManager;
+import android.view.ViewOutlineProvider;
 import android.view.ViewParent;
 import android.view.ViewStub;
 import android.widget.AdapterView.OnItemClickListener;
@@ -132,6 +134,13 @@
  *   <li>{@link android.widget.TextClock}</li>
  *   <li>{@link android.widget.TextView}</li>
  * </ul>
+ * <p>As of API 31, the following widgets and layouts may also be used:</p>
+ * <ul>
+ *     <li>{@link android.widget.CheckBox}</li>
+ *     <li>{@link android.widget.RadioButton}</li>
+ *     <li>{@link android.widget.RadioGroup}</li>
+ *     <li>{@link android.widget.Switch}</li>
+ * </ul>
  * <p>Descendants of these classes are not supported.</p>
  */
 public class RemoteViews implements Parcelable, Filter {
@@ -185,6 +194,9 @@
     private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
     private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
     private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
+    private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
+    private static final int SET_RADIO_GROUP_CHECKED = 27;
+    private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
 
     /** @hide **/
     @IntDef(prefix = "MARGIN_", value = {
@@ -2552,6 +2564,169 @@
         }
     }
 
+    private static class SetCompoundButtonCheckedAction extends Action {
+
+        private final boolean mChecked;
+
+        SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
+            this.viewId = viewId;
+            mChecked = checked;
+        }
+
+        SetCompoundButtonCheckedAction(Parcel in) {
+            viewId = in.readInt();
+            mChecked = in.readBoolean();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeBoolean(mChecked);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                throws ActionException {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!(target instanceof CompoundButton)) {
+                Log.w(LOG_TAG, "Cannot set checked to view "
+                        + viewId + " because it is not a CompoundButton");
+                return;
+            }
+
+            ((CompoundButton) target).setChecked(mChecked);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_COMPOUND_BUTTON_CHECKED_TAG;
+        }
+    }
+
+    private static class SetRadioGroupCheckedAction extends Action {
+
+        @IdRes private final int mCheckedId;
+
+        SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
+            this.viewId = viewId;
+            mCheckedId = checkedId;
+        }
+
+        SetRadioGroupCheckedAction(Parcel in) {
+            viewId = in.readInt();
+            mCheckedId = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeInt(mCheckedId);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                throws ActionException {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!(target instanceof RadioGroup)) {
+                Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+                return;
+            }
+
+            ((RadioGroup) target).check(mCheckedId);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_RADIO_GROUP_CHECKED;
+        }
+    }
+
+    private static class SetViewOutlinePreferredRadiusAction extends Action {
+
+        private final boolean mIsDimen;
+        private final int mValue;
+
+        SetViewOutlinePreferredRadiusAction(@IdRes int viewId, @DimenRes int dimenResId) {
+            this.viewId = viewId;
+            this.mIsDimen = true;
+            this.mValue = dimenResId;
+        }
+
+        SetViewOutlinePreferredRadiusAction(
+                @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+            this.viewId = viewId;
+            this.mIsDimen = false;
+            this.mValue = TypedValue.createComplexDimension(radius, units);
+
+        }
+
+        SetViewOutlinePreferredRadiusAction(Parcel in) {
+            viewId = in.readInt();
+            mIsDimen = in.readBoolean();
+            mValue = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+            dest.writeBoolean(mIsDimen);
+            dest.writeInt(mValue);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                throws ActionException {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            float radius;
+            if (mIsDimen) {
+                radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue);
+            } else {
+                radius = TypedValue.complexToDimensionPixelSize(mValue,
+                        target.getResources().getDisplayMetrics());
+            }
+            target.setOutlineProvider(new RemoteViewOutlineProvider(radius));
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_VIEW_OUTLINE_RADIUS_TAG;
+        }
+    }
+
+    /**
+     * OutlineProvider for a view with a radius set by
+     * {@link #setViewOutlinePreferredRadius(int, float, int)}.
+     */
+    public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
+
+        private final float mRadius;
+
+        public RemoteViewOutlineProvider(float radius) {
+            mRadius = radius;
+        }
+
+        /** Returns the corner radius used when providing the view outline. */
+        public float getRadius() {
+            return mRadius;
+        }
+
+        @Override
+        public void getOutline(@NonNull View view, @NonNull Outline outline) {
+            outline.setRoundRect(
+                    0 /*left*/,
+                    0 /* top */,
+                    view.getWidth() /* right */,
+                    view.getHeight() /* bottom */,
+                    mRadius);
+        }
+    }
+
     /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
@@ -2766,6 +2941,12 @@
                 return new ResourceReflectionAction(parcel);
             case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
                 return new ComplexUnitDimensionReflectionAction(parcel);
+            case SET_COMPOUND_BUTTON_CHECKED_TAG:
+                return new SetCompoundButtonCheckedAction(parcel);
+            case SET_RADIO_GROUP_CHECKED:
+                return new SetRadioGroupCheckedAction(parcel);
+            case SET_VIEW_OUTLINE_RADIUS_TAG:
+                return new SetViewOutlinePreferredRadiusAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -3501,6 +3682,28 @@
     }
 
     /**
+     * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
+     * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}. This outline may change shape
+     * during system transitions.
+     *
+     * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
+     * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
+     * display with a different density.
+     */
+    public void setViewOutlinePreferredRadius(
+            @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+        addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units));
+    }
+
+    /**
+     * Sets an OutlineProvider on the view whose corner radius is a dimension resource with
+     * {@code resId}. This outline may change shape during system transitions.
+     */
+    public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
+        addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId));
+    }
+
+    /**
      * Call a method taking one boolean on a view in the layout for this RemoteViews.
      *
      * @param viewId The id of the view on which to call the method.
@@ -3846,6 +4049,26 @@
     }
 
     /**
+     * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
+     *
+     * @param viewId The id of the view whose property to set.
+     * @param checked true to check the button, false to uncheck it.
+     */
+    public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
+        addAction(new SetCompoundButtonCheckedAction(viewId, checked));
+    }
+
+    /**
+     * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
+     *
+     * @param viewId The id of the view whose property to set.
+     * @param checkedId The unique id of the radio button to select in the group.
+     */
+    public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
+        addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
+    }
+
+    /**
      * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
      * used by the host when the widgets displayed on a light-background where foreground elements
      * and text can safely draw using a dark color without any additional background protection.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3295fd2..d3600ef 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@
 import android.graphics.Region.Op;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.text.Layout;
@@ -48,12 +49,14 @@
 import android.util.MathUtils;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.RemotableViewMethod;
 import android.view.SoundEffectConstants;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inspector.InspectableProperty;
+import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.R;
 
@@ -84,6 +87,7 @@
  * @attr ref android.R.styleable#Switch_thumbTextPadding
  * @attr ref android.R.styleable#Switch_track
  */
+@RemoteView
 public class Switch extends CompoundButton {
     private static final int THUMB_ANIMATION_DURATION = 250;
 
@@ -441,6 +445,7 @@
      *
      * @attr ref android.R.styleable#Switch_switchPadding
      */
+    @RemotableViewMethod
     public void setSwitchPadding(int pixels) {
         mSwitchPadding = pixels;
         requestLayout();
@@ -466,6 +471,7 @@
      *
      * @attr ref android.R.styleable#Switch_switchMinWidth
      */
+    @RemotableViewMethod
     public void setSwitchMinWidth(int pixels) {
         mSwitchMinWidth = pixels;
         requestLayout();
@@ -491,6 +497,7 @@
      *
      * @attr ref android.R.styleable#Switch_thumbTextPadding
      */
+    @RemotableViewMethod
     public void setThumbTextPadding(int pixels) {
         mThumbTextPadding = pixels;
         requestLayout();
@@ -533,10 +540,17 @@
      *
      * @attr ref android.R.styleable#Switch_track
      */
+    @RemotableViewMethod(asyncImpl = "setTrackResourceAsync")
     public void setTrackResource(@DrawableRes int resId) {
         setTrackDrawable(getContext().getDrawable(resId));
     }
 
+    /** @hide **/
+    public Runnable setTrackResourceAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setTrackDrawable(drawable);
+    }
+
     /**
      * Get the drawable used for the track that the switch slides within.
      *
@@ -550,6 +564,23 @@
     }
 
     /**
+     * Set the drawable used for the track that the switch slides within to the specified Icon.
+     *
+     * @param icon an Icon holding the desired track, or {@code null} to clear
+     *             the track
+     */
+    @RemotableViewMethod(asyncImpl = "setTrackIconAsync")
+    public void setTrackIcon(@Nullable Icon icon) {
+        setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setTrackIconAsync(@Nullable Icon icon) {
+        Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setTrackDrawable(track);
+    }
+
+    /**
      * Applies a tint to the track drawable. Does not modify the current
      * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -563,6 +594,7 @@
      * @see #getTrackTintList()
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setTrackTintList(@Nullable ColorStateList tint) {
         mTrackTintList = tint;
         mHasTrackTint = true;
@@ -607,6 +639,7 @@
      * @see #getTrackTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setTrackTintBlendMode(@Nullable BlendMode blendMode) {
         mTrackBlendMode = blendMode;
         mHasTrackTintMode = true;
@@ -686,10 +719,17 @@
      *
      * @attr ref android.R.styleable#Switch_thumb
      */
+    @RemotableViewMethod(asyncImpl = "setThumbResourceAsync")
     public void setThumbResource(@DrawableRes int resId) {
         setThumbDrawable(getContext().getDrawable(resId));
     }
 
+    /** @hide **/
+    public Runnable setThumbResourceAsync(@DrawableRes int resId) {
+        Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+        return () -> setThumbDrawable(drawable);
+    }
+
     /**
      * Get the drawable used for the switch "thumb" - the piece that the user
      * can physically touch and drag along the track.
@@ -704,6 +744,24 @@
     }
 
     /**
+     * Set the drawable used for the switch "thumb" - the piece that the user
+     * can physically touch and drag along the track - to the specified Icon.
+     *
+     * @param icon an Icon holding the desired thumb, or {@code null} to clear
+     *             the thumb
+     */
+    @RemotableViewMethod(asyncImpl = "setThumbIconAsync")
+    public void setThumbIcon(@Nullable Icon icon) {
+        setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+    }
+
+    /** @hide **/
+    public Runnable setThumbIconAsync(@Nullable Icon icon) {
+        Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+        return () -> setThumbDrawable(track);
+    }
+
+    /**
      * Applies a tint to the thumb drawable. Does not modify the current
      * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
      * <p>
@@ -717,6 +775,7 @@
      * @see #getThumbTintList()
      * @see Drawable#setTintList(ColorStateList)
      */
+    @RemotableViewMethod
     public void setThumbTintList(@Nullable ColorStateList tint) {
         mThumbTintList = tint;
         mHasThumbTint = true;
@@ -761,6 +820,7 @@
      * @see #getThumbTintMode()
      * @see Drawable#setTintBlendMode(BlendMode)
      */
+    @RemotableViewMethod
     public void setThumbTintBlendMode(@Nullable BlendMode blendMode) {
         mThumbBlendMode = blendMode;
         mHasThumbTintMode = true;
@@ -822,6 +882,7 @@
      *
      * @attr ref android.R.styleable#Switch_splitTrack
      */
+    @RemotableViewMethod
     public void setSplitTrack(boolean splitTrack) {
         mSplitTrack = splitTrack;
         invalidate();
@@ -852,6 +913,7 @@
      *
      * @attr ref android.R.styleable#Switch_textOn
      */
+    @RemotableViewMethod
     public void setTextOn(CharSequence textOn) {
         mTextOn = textOn;
         requestLayout();
@@ -875,6 +937,7 @@
      *
      * @attr ref android.R.styleable#Switch_textOff
      */
+    @RemotableViewMethod
     public void setTextOff(CharSequence textOff) {
         mTextOff = textOff;
         requestLayout();
@@ -889,6 +952,7 @@
      * @param showText {@code true} to display on/off text
      * @attr ref android.R.styleable#Switch_showText
      */
+    @RemotableViewMethod
     public void setShowText(boolean showText) {
         if (mShowText != showText) {
             mShowText = showText;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8cfbca8..fe37c53 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -497,9 +497,9 @@
     private TextUtils.TruncateAt mEllipsize;
 
     // A flag to indicate the cursor was hidden by IME.
-    private boolean mImeTemporarilyConsumesInput;
+    private boolean mImeIsConsumingInput;
 
-    // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}.
+    // Whether cursor is visible without regard to {@link mImeConsumesInput}.
     // {code true} is the default value.
     private boolean mCursorVisibleFromAttr = true;
 
@@ -8750,7 +8750,7 @@
                 }
             }
             if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) {
-                outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT;
+                outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT;
             }
             if (isMultilineInputType(outAttrs.inputType)) {
                 // Multi-line text editors should always show an enter key.
@@ -10506,8 +10506,8 @@
 
     /**
      * Set whether the cursor is visible. The default is true. Note that this property only
-     * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will
-     * be always invisible, visibility will be updated as the last state when IME does not consume
+     * makes sense for editable TextView. If IME is consuming the input, the cursor will always be
+     * invisible, visibility will be updated as the last state when IME does not consume
      * the input anymore.
      *
      * @see #isCursorVisible()
@@ -10521,20 +10521,20 @@
     }
 
     /**
-     * Sets the IME is temporarily consuming the input and make the cursor invisible if
-     * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible.
+     * Sets the IME is consuming the input and make the cursor invisible if {@code imeConsumesInput}
+     * is {@code true}. Otherwise, make the cursor visible.
      *
-     * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input
+     * @param imeConsumesInput {@code true} if IME is consuming the input
      *
      * @hide
      */
-    public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
-        mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput;
+    public void setImeConsumesInput(boolean imeConsumesInput) {
+        mImeIsConsumingInput = imeConsumesInput;
         updateCursorVisibleInternal();
     }
 
     private void updateCursorVisibleInternal()  {
-        boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput;
+        boolean visible = mCursorVisibleFromAttr && !mImeIsConsumingInput;
         if (visible && mEditor == null) return; // visible is the default value with no edit data
         createEditorIfNeeded();
         if (mEditor.mCursorVisible != visible) {
@@ -10550,7 +10550,7 @@
 
     /**
      * @return whether or not the cursor is visible (assuming this TextView is editable). This
-     * method may return {@code false} when the IME is temporarily consuming the input even if the
+     * method may return {@code false} when the IME is consuming the input even if the
      * {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)}
      * is called.
      *
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index 0152387..c2ee646 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -53,8 +53,10 @@
     public static final String KEY_CUR_TASK = "cur_task";
     /** Package of newly requested heavy-weight app. */
     public static final String KEY_NEW_APP = "new_app";
+    public static final String KEY_ACTIVITY_OPTIONS = "activity_options";
     
     IntentSender mStartIntent;
+    Bundle mActivityOptions;
     boolean mHasResult;
     String mCurApp;
     int mCurTask;
@@ -65,8 +67,9 @@
         super.onCreate(savedInstanceState);
         
         requestWindowFeature(Window.FEATURE_NO_TITLE);
-        
+
         mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT);
+        mActivityOptions = getIntent().getBundleExtra(KEY_ACTIVITY_OPTIONS);
         mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false);
         mCurApp = getIntent().getStringExtra(KEY_CUR_APP);
         mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0);
@@ -148,9 +151,9 @@
                 if (mHasResult) {
                     startIntentSenderForResult(mStartIntent, -1, null,
                             Intent.FLAG_ACTIVITY_FORWARD_RESULT,
-                            Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0);
+                            Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0, mActivityOptions);
                 } else {
-                    startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0);
+                    startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0, mActivityOptions);
                 }
             } catch (IntentSender.SendIntentException ex) {
                 Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index a5eb5f6..7aca36a 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -179,9 +179,10 @@
      *
      * @param changeId    the ID of the change that was overridden
      * @param packageName the app package name that was overridden
+     * @return {@code true} if an override existed
      * @throws SecurityException if overriding changes is not permitted
      */
-    void clearOverrideForTest(long changeId, String packageName);
+    boolean clearOverrideForTest(long changeId, String packageName);
 
     /**
      * Enables all compatibility changes that have enabledSinceTargetSdk ==
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index fc95275..af21f81 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -726,4 +726,43 @@
             }
         }
     }
+
+    /**
+     * A {@link ServiceConnector} that doesn't connect to anything.
+     *
+     * @param <T> the type of the {@link IInterface ipc interface} for the remote service
+     */
+    class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> {
+        {
+            completeExceptionally(new IllegalStateException("ServiceConnector is a no-op"));
+        }
+
+        @Override
+        public boolean run(@NonNull VoidJob<T> job) {
+            return false;
+        }
+
+        @Override
+        public AndroidFuture<Void> post(@NonNull VoidJob<T> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public AndroidFuture<T> connect() {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public void unbind() {}
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 3d896c8..6e9bc84 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,10 +18,12 @@
 
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
@@ -114,6 +116,8 @@
     public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
     public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
     public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
+    public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
+    public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -147,6 +151,8 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -191,6 +197,8 @@
             CUJ_LOCKSCREEN_PIN_DISAPPEAR,
             CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
             CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
+            CUJ_LAUNCHER_OPEN_ALL_APPS,
+            CUJ_LAUNCHER_ALL_APPS_SCROLL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -457,6 +465,10 @@
                 return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD";
             case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
                 return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD";
+            case CUJ_LAUNCHER_OPEN_ALL_APPS :
+                return "CUJ_LAUNCHER_OPEN_ALL_APPS";
+            case CUJ_LAUNCHER_ALL_APPS_SCROLL:
+                return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 8fe17fb..16b6f3a 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -43,8 +43,7 @@
      */
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         final double powerMah = mPowerEstimator.calculatePower(durationMs);
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index e76e34f..094724c 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -24,7 +24,6 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.SparseArray;
 
 import java.util.ArrayList;
@@ -89,26 +88,27 @@
         final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
                 false /* collectBatteryBroadcast */);
         batteryStatsHelper.create((Bundle) null);
-        final UserManager userManager = mContext.getSystemService(UserManager.class);
-        final List<UserHandle> asUsers = userManager.getUserProfiles();
-        final int n = asUsers.size();
-        SparseArray<UserHandle> users = new SparseArray<>(n);
-        for (int i = 0; i < n; ++i) {
-            UserHandle userHandle = asUsers.get(i);
-            users.put(userHandle.getIdentifier(), userHandle);
+        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), batteryStatsHelper, users));
+            results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper));
         }
         return results;
     }
 
     private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
-            BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) {
+            BatteryStatsHelper batteryStatsHelper) {
         // TODO(b/174186358): read extra power component number from configuration
         final int customPowerComponentCount = 0;
         final int customTimeComponentCount = 0;
@@ -128,8 +128,8 @@
 
         final List<PowerCalculator> powerCalculators = getPowerCalculators();
         for (PowerCalculator powerCalculator : powerCalculators) {
-            powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, query,
-                    users);
+            powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
+                    query);
         }
 
         return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 4c3b950..7d42de4 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -50,8 +50,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
             return;
         }
@@ -68,7 +67,7 @@
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
             calculateApp(app, total);
             if (app.getUid() == Process.BLUETOOTH_UID) {
-                app.setSystemComponent(true);
+                app.excludeFromBatteryUsageStats();
                 systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
             }
         }
diff --git a/core/java/com/android/internal/os/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS
new file mode 100644
index 0000000..5d3473c
--- /dev/null
+++ b/core/java/com/android/internal/os/CPU_OWNERS
@@ -0,0 +1,3 @@
+connoro@google.com
+dplotnikov@google.com
+rslawik@google.com  # Android Telemetry
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 11c8761..45d8128 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -17,81 +17,166 @@
 
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.UidBatteryConsumer;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.List;
 
 public class CpuPowerCalculator extends PowerCalculator {
     private static final String TAG = "CpuPowerCalculator";
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
-    private final PowerProfile mProfile;
+    private final int mNumCpuClusters;
+
+    // Time-in-state based CPU power estimation model computes the estimated power
+    // by adding up three components:
+    //   - CPU Active power:    the constant amount of charge consumed by the CPU when it is on
+    //   - Per Cluster power:   the additional amount of charge consumed by a CPU cluster
+    //                          when it is running
+    //   - Per frequency power: the additional amount of charge caused by dynamic frequency scaling
+
+    private final UsageBasedPowerEstimator mCpuActivePowerEstimator;
+    // One estimator per cluster
+    private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators;
+    // Multiple estimators per cluster: one per available scaling frequency. Note that different
+    // clusters have different sets of frequencies and corresponding power consumption averages.
+    private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimators;
+
+    private static class Result {
+        public long durationMs;
+        public double powerMah;
+        public long durationFgMs;
+        public String packageWithHighestDrain;
+    }
 
     public CpuPowerCalculator(PowerProfile profile) {
-        mProfile = profile;
+        mNumCpuClusters = profile.getNumCpuClusters();
+
+        mCpuActivePowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
+
+        mPerClusterPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters];
+        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+            mPerClusterPowerEstimators[cluster] = new UsageBasedPowerEstimator(
+                    profile.getAveragePowerForCpuCluster(cluster));
+        }
+
+        mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters][];
+        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+            final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster);
+            mPerCpuFreqPowerEstimators[cluster] = new UsageBasedPowerEstimator[speedsForCluster];
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                mPerCpuFreqPowerEstimators[cluster][speed] =
+                        new UsageBasedPowerEstimator(
+                                profile.getAveragePowerForCpuCore(cluster, speed));
+            }
+        }
     }
 
     @Override
-    protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-        final int statsType = BatteryStats.STATS_SINCE_CHARGED;
+        Result result = new Result();
+        final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+                builder.getUidBatteryConsumerBuilders();
+        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+            calculateApp(app, app.getBatteryStatsUid(), result);
+        }
+    }
 
-        long cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
-        final int numClusters = mProfile.getNumCpuClusters();
+    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
+        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
 
-        double cpuPowerMaUs = 0;
-        for (int cluster = 0; cluster < numClusters; cluster++) {
-            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
-            for (int speed = 0; speed < speedsForCluster; speed++) {
-                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
-                final double cpuSpeedStepPower = timeUs *
-                        mProfile.getAveragePowerForCpuCore(cluster, speed);
-                if (DEBUG) {
-                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
-                            + speed + " timeUs=" + timeUs + " power="
-                            + formatCharge(cpuSpeedStepPower / MICROSEC_IN_HR));
-                }
-                cpuPowerMaUs += cpuSpeedStepPower;
+        app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
+                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
+                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND,
+                        result.durationFgMs)
+                .setPackageWithHighestDrain(result.packageWithHighestDrain);
+    }
+
+    @Override
+    public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
+            long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
+        Result result = new Result();
+        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, statsType, result);
             }
         }
-        cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
-                PowerProfile.POWER_CPU_ACTIVE);
+    }
+
+    private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
+        calculatePowerAndDuration(u, statsType, result);
+
+        app.cpuPowerMah = result.powerMah;
+        app.cpuTimeMs = result.durationMs;
+        app.cpuFgTimeMs = result.durationFgMs;
+        app.packageWithHighestDrain = result.packageWithHighestDrain;
+    }
+
+    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
+        long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
+
+        // Constant battery drain when CPU is active
+        double powerMah = mCpuActivePowerEstimator.calculatePower(u.getCpuActiveTime());
+
+        // Additional per-cluster battery drain
         long[] cpuClusterTimes = u.getCpuClusterTimes();
         if (cpuClusterTimes != null) {
-            if (cpuClusterTimes.length == numClusters) {
-                for (int i = 0; i < numClusters; i++) {
-                    double power =
-                            cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
-                    cpuPowerMaUs += power;
+            if (cpuClusterTimes.length == mNumCpuClusters) {
+                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+                    double power = mPerClusterPowerEstimators[cluster]
+                            .calculatePower(cpuClusterTimes[cluster]);
+                    powerMah += power;
                     if (DEBUG) {
-                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
-                                + cpuClusterTimes[i] + " power="
-                                + formatCharge(power / MICROSEC_IN_HR));
+                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
+                                + " power=" + formatCharge(power));
                     }
                 }
             } else {
                 Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
-                        + numClusters + " actual # " + cpuClusterTimes.length);
+                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
             }
         }
-        final double cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
 
-        if (DEBUG && (cpuTimeMs != 0 || cpuPowerMah != 0)) {
-            Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + cpuTimeMs + " ms power="
-                    + formatCharge(cpuPowerMah));
+        // Additional per-frequency battery drain
+        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+                final double power =
+                        mPerCpuFreqPowerEstimators[cluster][speed].calculatePower(timeUs / 1000);
+                if (DEBUG) {
+                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+                            + speed + " timeUs=" + timeUs + " power="
+                            + formatCharge(power));
+                }
+                powerMah += power;
+            }
+        }
+
+        if (DEBUG && (durationMs != 0 || powerMah != 0)) {
+            Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power="
+                    + formatCharge(powerMah));
         }
 
         // Keep track of the package with highest drain.
         double highestDrain = 0;
         String packageWithHighestDrain = null;
-        long cpuFgTimeMs = 0;
+        long durationFgMs = 0;
         final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
         final int processStatsCount = processStats.size();
         for (int i = 0; i < processStatsCount; i++) {
             final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
             final String processName = processStats.keyAt(i);
-            cpuFgTimeMs += ps.getForegroundTime(statsType);
+            durationFgMs += ps.getForegroundTime(statsType);
 
             final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
                     + ps.getForegroundTime(statsType);
@@ -107,20 +192,19 @@
             }
         }
 
-
         // Ensure that the CPU times make sense.
-        if (cpuFgTimeMs > cpuTimeMs) {
-            if (DEBUG && cpuFgTimeMs > cpuTimeMs + 10000) {
+        if (durationFgMs > durationMs) {
+            if (DEBUG && durationFgMs > durationMs + 10000) {
                 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
             }
 
             // Statistics may not have been gathered yet.
-            cpuTimeMs = cpuFgTimeMs;
+            durationMs = durationFgMs;
         }
 
-        app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, cpuPowerMah)
-                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, cpuTimeMs)
-                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, cpuFgTimeMs)
-                .setPackageWithHighestDrain(packageWithHighestDrain);
+        result.durationMs = durationMs;
+        result.durationFgMs = durationFgMs;
+        result.powerMah = powerMah;
+        result.packageWithHighestDrain = packageWithHighestDrain;
     }
 }
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index 9ea934a..df25cda 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -45,8 +45,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java
index dcc8a15..4a4991b 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -49,8 +49,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         if (mPowerMah != 0) {
diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
index fa552e3..50331e3 100644
--- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
@@ -24,10 +24,7 @@
     }
 
     /** Returns whether total CPU time is measured. */
-    public static boolean isSupported() {
-        // TODO(b/174245730): Implement this check.
-        return true;
-    }
+    public static native boolean isSupported();
 
     /** Reads total CPU time from bpf map. */
     public static native boolean read(Callback callback);
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index df46058..21dcce9 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -26,8 +26,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         final double powerMah = calculatePower(batteryStats, rawRealtimeUs,
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index e3bd64d..22001d4 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -85,8 +85,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
 
         PowerAndDuration total = new PowerAndDuration();
 
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 1b07aa0..0aa54f5 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,5 +1,6 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file *Cpu* = file:CPU_OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 6ab8c90..362ca07 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -39,8 +39,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED) / 1000;
         final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs);
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 05fcc70..141363f 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -67,17 +67,10 @@
      * @param batteryStats  The recorded battery stats.
      * @param rawRealtimeUs The raw system realtime in microseconds.
      * @param rawUptimeUs   The raw system uptime in microseconds.
-     * @param statsType     The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this
-     *                      can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since
-     *                      {@link BatteryStats#STATS_CURRENT} and
-     *                      {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated.
-     * @param asUsers       An array of users for which the attribution is requested.  It may
-     *                      contain {@link UserHandle#USER_ALL} to indicate that the attribution
-     *                      should be performed for all users.
+     * @param query         The query parameters for the calculator.
      */
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -99,19 +92,6 @@
      */
     protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
                                       long rawUptimeUs, int statsType) {
-
-        // TODO(b/175156498): Temporary code during the transition from BatterySippers to
-        //  BatteryConsumers.
-        UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, u);
-        calculateApp(builder, u, rawRealtimeUs, rawUptimeUs, BatteryUsageStatsQuery.DEFAULT);
-        final UidBatteryConsumer uidBatteryConsumer = builder.build();
-        app.cpuPowerMah = uidBatteryConsumer.getConsumedPower(
-                UidBatteryConsumer.POWER_COMPONENT_CPU);
-        app.cpuTimeMs = uidBatteryConsumer.getUsageDurationMillis(
-                UidBatteryConsumer.TIME_COMPONENT_CPU);
-        app.cpuFgTimeMs = uidBatteryConsumer.getUsageDurationMillis(
-                UidBatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND);
-        app.packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
     }
 
     /**
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index c86c795..bb1222e 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -51,8 +51,7 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final long durationMs = computeDuration(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         final double powerMah = computePower(batteryStats, rawRealtimeUs,
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 55fc1bb..955f6af 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -51,10 +51,9 @@
 
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
-            SparseArray<UserHandle> asUsers) {
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         calculateSystemServicePower(batteryStats);
-        super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query, asUsers);
+        super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
     }
 
     @Override
diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java
index 53f8515..8e80286 100644
--- a/core/java/com/android/internal/os/UserPowerCalculator.java
+++ b/core/java/com/android/internal/os/UserPowerCalculator.java
@@ -17,10 +17,15 @@
 package com.android.internal.os;
 
 import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
+import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.util.List;
 
 /**
@@ -29,6 +34,33 @@
 public class UserPowerCalculator extends PowerCalculator {
 
     @Override
+    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+        final int[] userIds = query.getUserIds();
+        if (ArrayUtils.contains(userIds, UserHandle.USER_ALL)) {
+            return;
+        }
+
+        SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+                builder.getUidBatteryConsumerBuilders();
+
+        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+            UidBatteryConsumer.Builder uidBuilder = uidBatteryConsumerBuilders.valueAt(i);
+            final int uid = uidBuilder.getUid();
+            if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+                continue;
+            }
+
+            final int userId = UserHandle.getUserId(uid);
+            if (!ArrayUtils.contains(userIds, userId)) {
+                uidBuilder.excludeFromBatteryUsageStats();
+                builder.getOrCreateUserBatteryConsumerBuilder(userId)
+                        .addUidBatteryConsumer(uidBuilder);
+            }
+        }
+    }
+
+    @Override
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d196d4a..c8afea9 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1091,4 +1091,11 @@
      * fully-feature Memory Tagging, rather than the static Tagged Pointers.
      */
     public static native boolean nativeSupportsTaggedPointers();
+
+    /**
+     * Returns the current native tagging level, as one of the
+     * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
+     * we failed to determine the level.
+     */
+    public static native int nativeCurrentTaggingLevel();
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 94e21e5..f2ed50e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -787,9 +787,19 @@
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
             if (Zygote.nativeSupportsMemoryTagging()) {
-                /* The system server is more privileged than regular app processes, so it has async
-                 * tag checks enabled on hardware that supports memory tagging. */
-                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                /* The system server has ASYNC MTE by default, in order to allow
+                 * system services to specify their own MTE level later, as you
+                 * can't re-enable MTE once it's disabled. */
+                String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+                if (mode.equals("async")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                } else if (mode.equals("sync")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
+                } else if (!mode.equals("off")) {
+                    /* When we have an invalid memory tag level, keep the current level. */
+                    parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
+                    Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
+                }
             } else if (Zygote.nativeSupportsTaggedPointers()) {
                 /* Enable pointer tagging in the system server. Hardware support for this is present
                  * in all ARMv8 CPUs. */
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a4ce027..585ddf6 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@
                 long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
 
                 if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
-                    // Normalize the poll timeout value when the time between one poll event and the
-                    // next pushes us over the delay value.  This prevents poll receiving a 0
-                    // timeout value, which would result in it returning immediately.
-                    pollTimeoutMs = -1;
+                    // The refill delay has elapsed during the period between poll invocations.
+                    // We will now check for any currently ready file descriptors before refilling
+                    // the USAP pool.
+                    pollTimeoutMs = 0;
+                    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+                    mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
 
                 } else if (elapsedTimeMs <= 0) {
                     // This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@
             }
 
             if (pollReturnValue == 0) {
-                // The poll timeout has been exceeded.  This only occurs when we have finished the
-                // USAP pool refill delay period.
-
+                // The poll returned zero results either when the timeout value has been exceeded
+                // or when a non-blocking poll is issued and no FDs are ready.  In either case it
+                // is time to refill the pool.  This will result in a duplicate assignment when
+                // the non-blocking poll returns zero results, but it avoids an additional
+                // conditional in the else branch.
                 mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                 mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
 
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index e7b7bf4..19506a3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -79,7 +79,7 @@
     private static final int DO_CLOSE_CONNECTION = 150;
     private static final int DO_COMMIT_CONTENT = 160;
     private static final int DO_GET_SURROUNDING_TEXT = 41;
-    private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170;
+    private static final int DO_SET_IME_CONSUMES_INPUT = 170;
 
 
     @GuardedBy("mLock")
@@ -268,13 +268,12 @@
     }
 
     /**
-     * Dispatches the request for setting ime temporarily consumes input.
+     * Dispatches the request for setting ime consumes input.
      *
-     * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+     * <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
      */
-    public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
-        dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT,
-                imeTemporarilyConsumesInput));
+    public void setImeConsumesInput(boolean imeConsumesInput) {
+        dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
     }
 
     void dispatchMessage(Message msg) {
@@ -822,17 +821,17 @@
                 }
                 return;
             }
-            case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: {
+            case DO_SET_IME_CONSUMES_INPUT: {
                 Trace.traceBegin(Trace.TRACE_TAG_INPUT,
-                        "InputConnection#setImeTemporarilyConsumesInput");
+                        "InputConnection#setImeConsumesInput");
                 try {
                     InputConnection ic = getInputConnection();
                     if (ic == null || !isActive()) {
                         Log.w(TAG,
-                                "setImeTemporarilyConsumesInput on inactive InputConnection");
+                                "setImeConsumesInput on inactive InputConnection");
                         return;
                     }
-                    ic.setImeTemporarilyConsumesInput(msg.arg1 == 1);
+                    ic.setImeConsumesInput(msg.arg1 == 1);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 586404c..b06b4e5 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -86,5 +86,5 @@
     void getSurroundingText(int beforeLength, int afterLength, int flags,
             ISurroundingTextResultCallback callback);
 
-    void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput);
+    void setImeConsumesInput(boolean imeConsumesInput);
 }
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 84c92ca..0e9d135 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -525,12 +525,12 @@
     }
 
     /**
-     * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+     * See {@link InputConnection#setImeConsumesInput(boolean)}.
      */
     @AnyThread
-    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
         try {
-            mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+            mIInputContext.setImeConsumesInput(imeConsumesInput);
             return true;
         } catch (RemoteException e) {
             return false;
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
new file mode 100644
index 0000000..6cc5a4a
--- /dev/null
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -0,0 +1,137 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.app.Person;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * A custom-built layout for the Notification.CallStyle.
+ */
+@RemoteViews.RemoteView
+public class CallLayout extends FrameLayout {
+    private final PeopleHelper mPeopleHelper = new PeopleHelper();
+
+    private int mLayoutColor;
+    private Icon mLargeIcon;
+    private Person mUser;
+
+    private CachingIconView mConversationIconView;
+    private CachingIconView mIcon;
+    private CachingIconView mConversationIconBadgeBg;
+    private TextView mConversationText;
+
+    public CallLayout(@NonNull Context context) {
+        super(context);
+    }
+
+    public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mPeopleHelper.init(getContext());
+        mConversationText = findViewById(R.id.conversation_text);
+        mConversationIconView = findViewById(R.id.conversation_icon);
+        mIcon = findViewById(R.id.icon);
+        mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg);
+
+        // When the small icon is gone, hide the rest of the badge
+        mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+            mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+        });
+    }
+
+    private void updateCallLayout() {
+        CharSequence callerName = "";
+        String symbol = "";
+        Icon icon = null;
+        if (mUser != null) {
+            icon = mUser.getIcon();
+            callerName = mUser.getName();
+            symbol = mPeopleHelper.findNamePrefix(callerName, "");
+        }
+        if (icon == null) {
+            icon = mLargeIcon;
+        }
+        if (icon == null) {
+            icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
+        }
+        // TODO(b/179178086): crop/clip the icon to a circle?
+        mConversationIconView.setImageIcon(icon);
+        mConversationText.setText(callerName);
+    }
+
+    @RemotableViewMethod
+    public void setLayoutColor(int color) {
+        mLayoutColor = color;
+    }
+
+    /**
+     * @param color the color of the notification background
+     */
+    @RemotableViewMethod
+    public void setNotificationBackgroundColor(int color) {
+        mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color));
+    }
+
+    @RemotableViewMethod
+    public void setLargeIcon(Icon largeIcon) {
+        mLargeIcon = largeIcon;
+    }
+
+    /**
+     * Set the notification extras so that this layout has access
+     */
+    @RemotableViewMethod
+    public void setData(Bundle extras) {
+        setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON));
+        updateCallLayout();
+    }
+
+    private void setUser(Person user) {
+        mUser = user;
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 40e671f..1b1e0bf 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -18,8 +18,6 @@
 
 import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
 import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -34,10 +32,7 @@
 import android.app.RemoteInputHistoryItem;
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.GradientDrawable;
@@ -68,14 +63,12 @@
 
 import com.android.internal.R;
 import com.android.internal.graphics.ColorUtils;
-import com.android.internal.util.ContrastColorUtil;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.function.Consumer;
-import java.util.regex.Pattern;
 
 /**
  * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
@@ -85,16 +78,6 @@
 public class ConversationLayout extends FrameLayout
         implements ImageMessageConsumer, IMessagingLayout {
 
-    private static final float COLOR_SHIFT_AMOUNT = 60;
-    /**
-     *  Pattern for filter some ignorable characters.
-     *  p{Z} for any kind of whitespace or invisible separator.
-     *  p{C} for any kind of punctuation character.
-     */
-    private static final Pattern IGNORABLE_CHAR_PATTERN
-            = Pattern.compile("[\\p{C}\\p{Z}]");
-    private static final Pattern SPECIAL_CHAR_PATTERN
-            = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
     private static final Consumer<MessagingMessage> REMOVE_MESSAGE
             = MessagingMessage::removeMessage;
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
@@ -106,6 +89,7 @@
     public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
     public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
     public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
+    private final PeopleHelper mPeopleHelper = new PeopleHelper();
     private List<MessagingMessage> mMessages = new ArrayList<>();
     private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
     private MessagingLinearLayout mMessagingLinearLayout;
@@ -114,9 +98,6 @@
     private int mLayoutColor;
     private int mSenderTextColor;
     private int mMessageTextColor;
-    private int mAvatarSize;
-    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private Paint mTextPaint = new Paint();
     private Icon mAvatarReplacement;
     private boolean mIsOneToOne;
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
@@ -196,6 +177,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mPeopleHelper.init(getContext());
         mMessagingLinearLayout = findViewById(R.id.notification_messaging);
         mActions = findViewById(R.id.actions);
         mImageMessageContainer = findViewById(R.id.conversation_image_message_container);
@@ -205,9 +187,6 @@
         int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
         mMessagingClipRect = new Rect(0, 0, size, size);
         setMessagingClippingDisabled(false);
-        mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
-        mTextPaint.setTextAlign(Paint.Align.CENTER);
-        mTextPaint.setAntiAlias(true);
         mConversationIconView = findViewById(R.id.conversation_icon);
         mConversationIconContainer = findViewById(R.id.conversation_icon_container);
         mIcon = findViewById(R.id.icon);
@@ -250,15 +229,15 @@
         });
         // When the small icon is gone, hide the rest of the badge
         mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
-            animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
-            animateViewForceHidden(mImportanceRingView, forceHidden);
+            mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+            mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
         });
 
         // When the conversation icon is gone, hide the whole badge
         mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> {
-            animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
-            animateViewForceHidden(mImportanceRingView, forceHidden);
-            animateViewForceHidden(mIcon, forceHidden);
+            mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+            mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
+            mPeopleHelper.animateViewForceHidden(mIcon, forceHidden);
         });
         mConversationText = findViewById(R.id.conversation_text);
         mExpandButtonContainer = findViewById(R.id.expand_button_container);
@@ -317,28 +296,6 @@
                 R.dimen.notification_header_separating_margin);
     }
 
-    private void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
-        boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
-        if (forceHidden == nowForceHidden) {
-            // We are either already forceHidden or will be
-            return;
-        }
-        view.animate().cancel();
-        view.setWillBeForceHidden(forceHidden);
-        view.animate()
-                .scaleX(forceHidden ? 0.5f : 1.0f)
-                .scaleY(forceHidden ? 0.5f : 1.0f)
-                .alpha(forceHidden ? 0.0f : 1.0f)
-                .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
-                .setDuration(160);
-        if (view.getVisibility() != VISIBLE) {
-            view.setForceHidden(forceHidden);
-        } else {
-            view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
-        }
-        view.animate().start();
-    }
-
     @RemotableViewMethod
     public void setAvatarReplacement(Icon icon) {
         mAvatarReplacement = icon;
@@ -561,7 +518,8 @@
                     if (mConversationIcon == null) {
                         Icon avatarIcon = messagingGroup.getAvatarIcon();
                         if (avatarIcon == null) {
-                            avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+                            avatarIcon = mPeopleHelper.createAvatarSymbol(conversationText, "",
+                                    mLayoutColor);
                         }
                         mConversationIcon = avatarIcon;
                     }
@@ -674,11 +632,11 @@
             }
         }
         if (lastIcon == null) {
-            lastIcon = createAvatarSymbol(" ", "", mLayoutColor);
+            lastIcon = mPeopleHelper.createAvatarSymbol(" ", "", mLayoutColor);
         }
         bottomView.setImageIcon(lastIcon);
         if (secondLastIcon == null) {
-            secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
+            secondLastIcon = mPeopleHelper.createAvatarSymbol("", "", mLayoutColor);
         }
         topView.setImageIcon(secondLastIcon);
     }
@@ -838,8 +796,10 @@
     }
 
     private void updateTitleAndNamesDisplay() {
+        // Map of unique names to their prefix
         ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>();
-        ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>();
+        // Map of single-character string prefix to the only name which uses it, or null if multiple
+        ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>();
         for (int i = 0; i < mGroups.size(); i++) {
             MessagingGroup group = mGroups.get(i);
             CharSequence senderName = group.getSenderName();
@@ -847,22 +807,22 @@
                 continue;
             }
             if (!uniqueNames.containsKey(senderName)) {
-                // Only use visible characters to get uniqueNames
-                String pureSenderName = IGNORABLE_CHAR_PATTERN
-                        .matcher(senderName).replaceAll("" /* replacement */);
-                char c = pureSenderName.charAt(0);
-                if (uniqueCharacters.containsKey(c)) {
+                String charPrefix = mPeopleHelper.findNamePrefix(senderName, null);
+                if (charPrefix == null) {
+                    continue;
+                }
+                if (uniqueCharacters.containsKey(charPrefix)) {
                     // this character was already used, lets make it more unique. We first need to
                     // resolve the existing character if it exists
-                    CharSequence existingName = uniqueCharacters.get(c);
+                    CharSequence existingName = uniqueCharacters.get(charPrefix);
                     if (existingName != null) {
-                        uniqueNames.put(existingName, findNameSplit((String) existingName));
-                        uniqueCharacters.put(c, null);
+                        uniqueNames.put(existingName, mPeopleHelper.findNameSplit(existingName));
+                        uniqueCharacters.put(charPrefix, null);
                     }
-                    uniqueNames.put(senderName, findNameSplit((String) senderName));
+                    uniqueNames.put(senderName, mPeopleHelper.findNameSplit(senderName));
                 } else {
-                    uniqueNames.put(senderName, Character.toString(c));
-                    uniqueCharacters.put(c, pureSenderName);
+                    uniqueNames.put(senderName, charPrefix);
+                    uniqueCharacters.put(charPrefix, senderName);
                 }
             }
         }
@@ -898,8 +858,8 @@
             } else {
                 Icon cachedIcon = cachedAvatars.get(senderName);
                 if (cachedIcon == null) {
-                    cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName),
-                            mLayoutColor);
+                    cachedIcon = mPeopleHelper.createAvatarSymbol(senderName,
+                            uniqueNames.get(senderName), mLayoutColor);
                     cachedAvatars.put(senderName, cachedIcon);
                 }
                 group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName),
@@ -908,49 +868,6 @@
         }
     }
 
-    private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
-        if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
-                SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
-            Icon avatarIcon = Icon.createWithResource(getContext(),
-                    R.drawable.messaging_user);
-            avatarIcon.setTint(findColor(senderName, layoutColor));
-            return avatarIcon;
-        } else {
-            Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(bitmap);
-            float radius = mAvatarSize / 2.0f;
-            int color = findColor(senderName, layoutColor);
-            mPaint.setColor(color);
-            canvas.drawCircle(radius, radius, radius, mPaint);
-            boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
-            mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
-            mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
-            int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
-            canvas.drawText(symbol, radius, yPos, mTextPaint);
-            return Icon.createWithBitmap(bitmap);
-        }
-    }
-
-    private int findColor(CharSequence senderName, int layoutColor) {
-        double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
-        float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
-
-        // we need to offset the range if the luminance is too close to the borders
-        shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
-        shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
-        return ContrastColorUtil.getShiftedColor(layoutColor,
-                (int) (shift * COLOR_SHIFT_AMOUNT));
-    }
-
-    private String findNameSplit(String existingName) {
-        String[] split = existingName.split(" ");
-        if (split.length > 1) {
-            return Character.toString(split[0].charAt(0))
-                    + Character.toString(split[1].charAt(0));
-        }
-        return existingName.substring(0, 1);
-    }
-
     @RemotableViewMethod
     public void setLayoutColor(int color) {
         mLayoutColor = color;
diff --git a/core/java/com/android/internal/widget/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java
new file mode 100644
index 0000000..0d9bf71
--- /dev/null
+++ b/core/java/com/android/internal/widget/DisableImageView.java
@@ -0,0 +1,68 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
+ */
+@RemoteViews.RemoteView
+public class DisableImageView extends ImageView {
+
+    public DisableImageView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        // Apply the disabled filter
+        ColorMatrix brightnessMatrix = new ColorMatrix();
+        float brightnessF = 0.5f;
+        int brightnessI = (int) (255 * brightnessF);
+        // Brightness: C-new = C-old*(1-amount) + amount
+        float scale = 1f - brightnessF;
+        float[] mat = brightnessMatrix.getArray();
+        mat[0] = scale;
+        mat[6] = scale;
+        mat[12] = scale;
+        mat[4] = brightnessI;
+        mat[9] = brightnessI;
+        mat[14] = brightnessI;
+
+        ColorMatrix filterMatrix = new ColorMatrix();
+        filterMatrix.setSaturation(0);
+        filterMatrix.preConcat(brightnessMatrix);
+        setColorFilter(new ColorMatrixColorFilter(filterMatrix));
+    }
+}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 4ccf9ce..3d054a5 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -245,11 +245,11 @@
     }
 
     @Override
-    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+    public boolean setImeConsumesInput(boolean imeConsumesInput) {
         if (mTextView == null) {
-            return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+            return super.setImeConsumesInput(imeConsumesInput);
         }
-        mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+        mTextView.setImeConsumesInput(imeConsumesInput);
         return true;
     }
 
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 5213746..058a921 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -16,17 +16,24 @@
 
 package com.android.internal.widget;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.BlendMode;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableWrapper;
 import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.Icon;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.RemotableViewMethod;
+import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
+
 /**
  * A button implementation for the emphasized notification style.
  *
@@ -37,6 +44,7 @@
     private final RippleDrawable mRipple;
     private final int mStrokeWidth;
     private final int mStrokeColor;
+    private boolean mPriority;
 
     public EmphasizedNotificationButton(Context context) {
         this(context, null);
@@ -80,4 +88,57 @@
         inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
         invalidate();
     }
+
+    /**
+     * Sets an image icon which will have its size constrained and will be set to the same color as
+     * the text. Must be called after {@link #setTextColor(int)} for the latter to work.
+     */
+    @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+    public void setImageIcon(@Nullable Icon icon) {
+        final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+        setImageDrawable(drawable);
+    }
+
+    /**
+     * @hide
+     */
+    @RemotableViewMethod
+    public Runnable setImageIconAsync(@Nullable Icon icon) {
+        final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+        return () -> setImageDrawable(drawable);
+    }
+
+    private void setImageDrawable(Drawable drawable) {
+        if (drawable != null) {
+            drawable.mutate();
+            drawable.setTintList(getTextColors());
+            drawable.setTintBlendMode(BlendMode.SRC_IN);
+            int iconSize = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.notification_actions_icon_drawable_size);
+            drawable.setBounds(0, 0, iconSize, iconSize);
+        }
+        setCompoundDrawablesRelative(drawable, null, null, null);
+    }
+
+    /**
+     * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
+     * is a priority over its peers (which affects weight).
+     */
+    @RemotableViewMethod
+    public void setWrapModePriority(boolean priority) {
+        mPriority = priority;
+        ViewGroup.LayoutParams layoutParams = getLayoutParams();
+        layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+        if (layoutParams instanceof LinearLayout.LayoutParams) {
+            ((LinearLayout.LayoutParams) layoutParams).weight = 0;
+        }
+        setLayoutParams(layoutParams);
+    }
+
+    /**
+     * Sizing this button is a priority compared with its peers.
+     */
+    public boolean isPriority() {
+        return mPriority;
+    }
 }
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index c7ea781..8e6497b 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.annotation.DimenRes;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.RippleDrawable;
@@ -41,13 +42,16 @@
 
     private final int mGravity;
     private int mTotalWidth = 0;
+    private int mExtraStartPadding = 0;
     private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
     private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
     private boolean mEmphasizedMode;
+    private boolean mPrioritizedWrapMode;
     private int mDefaultPaddingBottom;
     private int mDefaultPaddingTop;
     private int mEmphasizedHeight;
     private int mRegularHeight;
+    @DimenRes private int mCollapsibleIndentDimen;
 
     public NotificationActionListLayout(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -68,7 +72,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mEmphasizedMode) {
+        if (mEmphasizedMode && !mPrioritizedWrapMode) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
             return;
         }
@@ -151,7 +155,15 @@
             measuredChildren++;
         }
 
-        mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
+        int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
+                : getResources().getDimensionPixelOffset(mCollapsibleIndentDimen);
+        if (innerWidth - usedWidth > collapsibleIndent) {
+            mExtraStartPadding = collapsibleIndent;
+        } else {
+            mExtraStartPadding = 0;
+        }
+
+        mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
         setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                 resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
     }
@@ -163,7 +175,11 @@
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View c = getChildAt(i);
-            if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+            if (c instanceof EmphasizedNotificationButton
+                    && ((EmphasizedNotificationButton) c).isPriority()) {
+                // add with 0 length to ensure that this view is measured before others.
+                mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
+            } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
                 mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
                         (TextView)c));
             } else {
@@ -197,7 +213,7 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mEmphasizedMode) {
+        if (mEmphasizedMode && !mPrioritizedWrapMode) {
             super.onLayout(changed, left, top, right, bottom);
             return;
         }
@@ -214,6 +230,9 @@
             int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection());
             if (absoluteGravity == Gravity.RIGHT) {
                 childLeft += right - left - mTotalWidth;
+            } else {
+                // Put the extra start padding (if any) on the left when LTR
+                childLeft += mExtraStartPadding;
             }
         }
 
@@ -274,6 +293,26 @@
     }
 
     /**
+     * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
+     * buttons (which are system generated) to not scrunch, and leave the remaining space for
+     * custom actions.
+     */
+    @RemotableViewMethod
+    public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
+        mPrioritizedWrapMode = prioritizedWrapMode;
+    }
+
+    /**
+     * When buttons are in wrap mode, this is a padding that will be applied at the start of the
+     * layout of the actions, but only when those actions would fit with the entire padding
+     * visible.  Otherwise, this padding will be omitted entirely.
+     */
+    @RemotableViewMethod
+    public void setCollapsibleIndentDimen(@DimenRes int collapsibleIndentDimen) {
+        mCollapsibleIndentDimen = collapsibleIndentDimen;
+    }
+
+    /**
      * Set whether the list is in a mode where some actions are emphasized. This will trigger an
      * equal measuring where all actions are full height and change a few parameters like
      * the padding.
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@
 per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
 per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java
new file mode 100644
index 0000000..77f4c8f
--- /dev/null
+++ b/core/java/com/android/internal/widget/PeopleHelper.java
@@ -0,0 +1,179 @@
+/*
+ * 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.internal.widget;
+
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class provides some methods used by both the {@link ConversationLayout} and
+ * {@link CallLayout} which both use the visual design originally created for conversations in R.
+ */
+public class PeopleHelper {
+
+    private static final float COLOR_SHIFT_AMOUNT = 60;
+    /**
+     * Pattern for filter some ignorable characters.
+     * p{Z} for any kind of whitespace or invisible separator.
+     * p{C} for any kind of punctuation character.
+     */
+    private static final Pattern IGNORABLE_CHAR_PATTERN = Pattern.compile("[\\p{C}\\p{Z}]");
+    private static final Pattern SPECIAL_CHAR_PATTERN =
+            Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
+
+    private Context mContext;
+    private int mAvatarSize;
+    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private Paint mTextPaint = new Paint();
+
+    /**
+     * Call this when the view is inflated to provide a context and initialize the helper
+     */
+    public void init(Context context) {
+        mContext = context;
+        mAvatarSize = context.getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+        mTextPaint.setTextAlign(Paint.Align.CENTER);
+        mTextPaint.setAntiAlias(true);
+    }
+
+    /**
+     * A utility for animating CachingIconViews away when hidden.
+     */
+    public void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
+        boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
+        if (forceHidden == nowForceHidden) {
+            // We are either already forceHidden or will be
+            return;
+        }
+        view.animate().cancel();
+        view.setWillBeForceHidden(forceHidden);
+        view.animate()
+                .scaleX(forceHidden ? 0.5f : 1.0f)
+                .scaleY(forceHidden ? 0.5f : 1.0f)
+                .alpha(forceHidden ? 0.0f : 1.0f)
+                .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
+                .setDuration(160);
+        if (view.getVisibility() != View.VISIBLE) {
+            view.setForceHidden(forceHidden);
+        } else {
+            view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
+        }
+        view.animate().start();
+    }
+
+    /**
+     * This creates an avatar symbol for the given person or group
+     *
+     * @param name        the name of the person or group
+     * @param symbol      a pre-chosen symbol for the person or group.  See
+     *                    {@link #findNamePrefix(CharSequence, String)} or
+     *                    {@link #findNameSplit(CharSequence)}
+     * @param layoutColor the background color of the layout
+     */
+    @NonNull
+    public Icon createAvatarSymbol(@NonNull CharSequence name, @NonNull String symbol,
+            @ColorInt int layoutColor) {
+        if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol)
+                || SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+            Icon avatarIcon = Icon.createWithResource(mContext, R.drawable.messaging_user);
+            avatarIcon.setTint(findColor(name, layoutColor));
+            return avatarIcon;
+        } else {
+            Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            float radius = mAvatarSize / 2.0f;
+            int color = findColor(name, layoutColor);
+            mPaint.setColor(color);
+            canvas.drawCircle(radius, radius, radius, mPaint);
+            boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+            mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+            mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+            int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+            canvas.drawText(symbol, radius, yPos, mTextPaint);
+            return Icon.createWithBitmap(bitmap);
+        }
+    }
+
+    private int findColor(@NonNull CharSequence senderName, int layoutColor) {
+        double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
+        float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
+
+        // we need to offset the range if the luminance is too close to the borders
+        shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
+        shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
+        return ContrastColorUtil.getShiftedColor(layoutColor,
+                (int) (shift * COLOR_SHIFT_AMOUNT));
+    }
+
+    /**
+     * Get the name with whitespace and punctuation characters removed
+     */
+    private String getPureName(@NonNull CharSequence name) {
+        return IGNORABLE_CHAR_PATTERN.matcher(name).replaceAll("" /* replacement */);
+    }
+
+    /**
+     * Gets a single character string prefix name for the person or group
+     *
+     * @param name     the name of the person or group
+     * @param fallback the string to return if the name has no usable characters
+     */
+    public String findNamePrefix(@NonNull CharSequence name, String fallback) {
+        String pureName = getPureName(name);
+        if (pureName.isEmpty()) {
+            return fallback;
+        }
+        try {
+            return new String(Character.toChars(pureName.codePointAt(0)));
+        } catch (RuntimeException ignore) {
+            return fallback;
+        }
+    }
+
+    /**
+     * Find a 1 or 2 character prefix name for the person or group
+     */
+    public String findNameSplit(@NonNull CharSequence name) {
+        String nameString = name instanceof String ? ((String) name) : name.toString();
+        String[] split = nameString.trim().split("[ ]+");
+        if (split.length > 1) {
+            String first = findNamePrefix(split[0], null);
+            String second = findNamePrefix(split[1], null);
+            if (first != null && second != null) {
+                return first + second;
+            }
+        }
+        return findNamePrefix(name, "");
+    }
+}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index b4d8e50..95999a7 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.os.DropBoxManager;
 import android.os.Environment;
-import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.RecoverySystem;
 import android.os.RemoteException;
@@ -74,7 +73,6 @@
         SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
     private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
 
-    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
     private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
 
     // The pre-froyo package and class of the system updater, which
@@ -85,9 +83,6 @@
     private static final String OLD_UPDATER_CLASS =
         "com.google.android.systemupdater.SystemUpdateReceiver";
 
-    // Keep a reference to the observer so the finalizer doesn't disable it.
-    private static FileObserver sTombstoneObserver = null;
-
     private static final String LOG_FILES_FILE = "log-files.xml";
     private static final AtomicFile sFile = new AtomicFile(new File(
             Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -153,7 +148,7 @@
         Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
     }
 
-    private String getPreviousBootHeaders() {
+    private static String getPreviousBootHeaders() {
         try {
             return FileUtils.readTextFile(lastHeaderFile, 0, null);
         } catch (IOException e) {
@@ -161,7 +156,7 @@
         }
     }
 
-    private String getCurrentBootHeaders() throws IOException {
+    private static String getCurrentBootHeaders() throws IOException {
         return new StringBuilder(512)
             .append("Build: ").append(Build.FINGERPRINT).append("\n")
             .append("Hardware: ").append(Build.BOARD).append("\n")
@@ -175,7 +170,7 @@
     }
 
 
-    private String getBootHeadersToLogAndUpdate() throws IOException {
+    private static String getBootHeadersToLogAndUpdate() throws IOException {
         final String oldHeaders = getPreviousBootHeaders();
         final String newHeaders = getCurrentBootHeaders();
 
@@ -247,38 +242,27 @@
         logFsMountTime();
         addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
         logSystemServerShutdownTimeMetrics();
-
-        // Scan existing tombstones (in case any new ones appeared)
-        File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
-        for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
-            if (tombstoneFiles[i].isFile()) {
-                addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
-                        LOG_SIZE, "SYSTEM_TOMBSTONE");
-            }
-        }
-
         writeTimestamps(timestamps);
+    }
 
-        // Start watching for new tombstone files; will record them as they occur.
-        // This gets registered with the singleton file observer thread.
-        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
-            @Override
-            public void onEvent(int event, String path) {
-                HashMap<String, Long> timestamps = readTimestamps();
-                try {
-                    File file = new File(TOMBSTONE_DIR, path);
-                    if (file.isFile() && file.getName().startsWith("tombstone_")) {
-                        addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
-                                TAG_TOMBSTONE);
-                    }
-                } catch (IOException e) {
-                    Slog.e(TAG, "Can't log tombstone", e);
-                }
-                writeTimestamps(timestamps);
-            }
-        };
-
-        sTombstoneObserver.startWatching();
+    /**
+     * Add a tombstone to the DropBox.
+     *
+     * @param ctx Context
+     * @param tombstone path to the tombstone
+     */
+    public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+        final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+        final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+        HashMap<String, Long> timestamps = readTimestamps();
+        try {
+            final String headers = getBootHeadersToLogAndUpdate();
+            addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+                    TAG_TOMBSTONE);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't log tombstone", e);
+        }
+        writeTimestamps(timestamps);
     }
 
     private static void addLastkToDropBox(
@@ -761,7 +745,7 @@
         }
     }
 
-    private void writeTimestamps(HashMap<String, Long> timestamps) {
+    private static void writeTimestamps(HashMap<String, Long> timestamps) {
         synchronized (sFile) {
             final FileOutputStream stream;
             try {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0b48e72..8edc8a1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,9 +207,9 @@
             ],
 
             shared_libs: [
-                "audioclient-types-aidl-unstable-cpp",
-                "audioflinger-aidl-unstable-cpp",
-                "av-types-aidl-unstable-cpp",
+                "audioclient-types-aidl-cpp",
+                "audioflinger-aidl-cpp",
+                "av-types-aidl-cpp",
                 "libandroidicu",
                 "libbpf_android",
                 "libnetdbpf",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 2279d57..35d1d7b 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -5,6 +5,9 @@
 # Connectivity
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
+
 # Display
 per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bb51c57..c64174b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -43,6 +43,7 @@
 #include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
+#include <dmabufinfo/dmabuf_sysfs_stats.h>
 #include <dmabufinfo/dmabufinfo.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
@@ -805,6 +806,16 @@
     return heapsSizeKb;
 }
 
+static jlong android_os_Debug_getDmabufTotalExportedKb(JNIEnv* env, jobject clazz) {
+    jlong dmabufTotalSizeKb = -1;
+    uint64_t size;
+
+    if (dmabufinfo::GetDmabufTotalExportedKb(&size)) {
+        dmabufTotalSizeKb = size;
+    }
+    return dmabufTotalSizeKb;
+}
+
 static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
     jlong poolsSizeKb = -1;
     uint64_t size;
@@ -816,8 +827,19 @@
     return poolsSizeKb;
 }
 
-static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) {
-    jlong ionPss = 0;
+static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject clazz) {
+    jlong poolsSizeKb = -1;
+    uint64_t size;
+
+    if (meminfo::ReadDmabufHeapPoolsSizeKb(&size)) {
+        poolsSizeKb = size;
+    }
+
+    return poolsSizeKb;
+}
+
+static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) {
+    jlong dmabufPss = 0;
     std::vector<dmabufinfo::DmaBuffer> dmabufs;
 
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
@@ -841,10 +863,10 @@
     }
 
     for (const dmabufinfo::DmaBuffer& buf : dmabufs) {
-        ionPss += buf.size() / 1024;
+        dmabufPss += buf.size() / 1024;
     }
 
-    return ionPss;
+    return dmabufPss;
 }
 
 static jlong android_os_Debug_getGpuTotalUsageKb(JNIEnv* env, jobject clazz) {
@@ -922,10 +944,14 @@
             (void*)android_os_Debug_getFreeZramKb },
     { "getIonHeapsSizeKb", "()J",
             (void*)android_os_Debug_getIonHeapsSizeKb },
+    { "getDmabufTotalExportedKb", "()J",
+            (void*)android_os_Debug_getDmabufTotalExportedKb },
     { "getIonPoolsSizeKb", "()J",
             (void*)android_os_Debug_getIonPoolsSizeKb },
-    { "getIonMappedSizeKb", "()J",
-            (void*)android_os_Debug_getIonMappedSizeKb },
+    { "getDmabufMappedSizeKb", "()J",
+            (void*)android_os_Debug_getDmabufMappedSizeKb },
+    { "getDmabufHeapPoolsSizeKb", "()J",
+            (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
     { "getGpuTotalUsageKb", "()J",
             (void*)android_os_Debug_getGpuTotalUsageKb },
     { "isVmapStack", "()Z",
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 6337680..8977b97 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -40,7 +40,7 @@
 
     jmethodID dispatchVsync;
     jmethodID dispatchHotplug;
-    jmethodID dispatchConfigChanged;
+    jmethodID dispatchModeChanged;
     jmethodID dispatchFrameRateOverrides;
 
     struct {
@@ -69,8 +69,8 @@
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                        VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                               int32_t configId, nsecs_t vsyncPeriod) override;
+    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+                             nsecs_t vsyncPeriod) override;
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
                                     std::vector<FrameRateOverride> overrides) override;
     void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
@@ -129,20 +129,19 @@
     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
 }
 
-void NativeDisplayEventReceiver::dispatchConfigChanged(
-    nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
-  JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                                     int32_t modeId, nsecs_t) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-  ScopedLocalRef<jobject> receiverObj(env,
-                                      jniGetReferent(env, mReceiverWeakGlobal));
-  if (receiverObj.get()) {
-    ALOGV("receiver %p ~ Invoking config changed handler.", this);
-    env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
-                        timestamp, displayId.value, configId);
-    ALOGV("receiver %p ~ Returned from config changed handler.", this);
-  }
+    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking mode changed handler.", this);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeChanged,
+                            timestamp, displayId.value, modeId);
+        ALOGV("receiver %p ~ Returned from mode changed handler.", this);
+    }
 
-  mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+    mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
 void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
@@ -173,7 +172,7 @@
         ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this);
     }
 
-    mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+    mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
@@ -243,8 +242,9 @@
                              "(JJIJJ)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
-    gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
-           gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
+    gDisplayEventReceiverClassInfo.dispatchModeChanged =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
+                             "(JJI)V");
     gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
                              "dispatchFrameRateOverrides",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 05fcaec..7a3366a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -44,8 +44,8 @@
 #include <ui/BlurRegion.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DeviceProductInfo.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
@@ -98,8 +98,8 @@
     jfieldID refreshRate;
     jfieldID appVsyncOffsetNanos;
     jfieldID presentationDeadlineNanos;
-    jfieldID configGroup;
-} gDisplayConfigClassInfo;
+    jfieldID group;
+} gDisplayModeClassInfo;
 
 static struct {
     jfieldID bottom;
@@ -202,13 +202,13 @@
 static struct {
     jclass clazz;
     jmethodID ctor;
-    jfieldID defaultConfig;
+    jfieldID defaultMode;
     jfieldID allowGroupSwitching;
     jfieldID primaryRefreshRateMin;
     jfieldID primaryRefreshRateMax;
     jfieldID appRequestRefreshRateMin;
     jfieldID appRequestRefreshRateMax;
-} gDesiredDisplayConfigSpecsClassInfo;
+} gDesiredDisplayModeSpecsClassInfo;
 
 static struct {
     jclass clazz;
@@ -1028,101 +1028,97 @@
     return object;
 }
 
-static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) {
-    Vector<DisplayConfig> configs;
+static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) {
+    Vector<ui::DisplayMode> modes;
     if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
-        SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
-        configs.isEmpty()) {
+        SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) {
         return nullptr;
     }
 
-    jobjectArray configArray =
-            env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr);
+    jobjectArray modesArray =
+            env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr);
 
-    for (size_t c = 0; c < configs.size(); ++c) {
-        const DisplayConfig& config = configs[c];
-        jobject object =
-                env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor);
-        env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth());
-        env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight());
-        env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi);
-        env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi);
+    for (size_t c = 0; c < modes.size(); ++c) {
+        const ui::DisplayMode& mode = modes[c];
+        jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
+        env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth());
+        env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight());
+        env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi);
+        env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi);
 
-        env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate);
-        env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos,
-                          config.appVsyncOffset);
-        env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
-                          config.presentationDeadline);
-        env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup);
-        env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
+        env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate);
+        env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset);
+        env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
+                          mode.presentationDeadline);
+        env->SetIntField(object, gDisplayModeClassInfo.group, mode.group);
+        env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object);
         env->DeleteLocalRef(object);
     }
 
-    return configArray;
+    return modesArray;
 }
 
-static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
-                                                   jobject desiredDisplayConfigSpecs) {
+static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
+                                                 jobject DesiredDisplayModeSpecs) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == nullptr) return JNI_FALSE;
 
-    jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
-                                          gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
+    size_t defaultMode =
+            static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs,
+                                                 gDesiredDisplayModeSpecsClassInfo.defaultMode));
     jboolean allowGroupSwitching =
-            env->GetBooleanField(desiredDisplayConfigSpecs,
-                                 gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching);
+            env->GetBooleanField(DesiredDisplayModeSpecs,
+                                 gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
     jfloat primaryRefreshRateMin =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin);
     jfloat primaryRefreshRateMax =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax);
     jfloat appRequestRefreshRateMin =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin);
     jfloat appRequestRefreshRateMax =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax);
 
-    size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
-                                                                        allowGroupSwitching,
-                                                                        primaryRefreshRateMin,
-                                                                        primaryRefreshRateMax,
-                                                                        appRequestRefreshRateMin,
-                                                                        appRequestRefreshRateMax);
+    size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
+                                                                      allowGroupSwitching,
+                                                                      primaryRefreshRateMin,
+                                                                      primaryRefreshRateMax,
+                                                                      appRequestRefreshRateMin,
+                                                                      appRequestRefreshRateMax);
     return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
 }
 
-static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == nullptr) return nullptr;
 
-    int32_t defaultConfig;
+    size_t defaultMode;
     bool allowGroupSwitching;
     float primaryRefreshRateMin;
     float primaryRefreshRateMax;
     float appRequestRefreshRateMin;
     float appRequestRefreshRateMax;
-    if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
-                                                            &allowGroupSwitching,
-                                                            &primaryRefreshRateMin,
-                                                            &primaryRefreshRateMax,
-                                                            &appRequestRefreshRateMin,
-                                                            &appRequestRefreshRateMax) !=
-        NO_ERROR) {
+    if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
+                                                          &primaryRefreshRateMin,
+                                                          &primaryRefreshRateMax,
+                                                          &appRequestRefreshRateMin,
+                                                          &appRequestRefreshRateMax) != NO_ERROR) {
         return nullptr;
     }
 
-    return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
-                          gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
-                          allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax,
-                          appRequestRefreshRateMin, appRequestRefreshRateMax);
+    return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
+                          gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
+                          primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
+                          appRequestRefreshRateMax);
 }
 
-static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return -1;
-    return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
+    return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token));
 }
 
 static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
@@ -1409,16 +1405,6 @@
     transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
 }
 
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
-        jlong nativeObject,
-        jlong newParentObject) {
-
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-    transaction->reparentChildren(ctrl, newParent);
-}
-
 static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject,
         jlong newParentObject) {
@@ -1794,16 +1780,16 @@
             (void*)nativeSetDisplaySize },
     {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
             (void*)nativeGetDisplayInfo },
-    {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;",
-            (void*)nativeGetDisplayConfigs },
-    {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
-            (void*)nativeGetActiveConfig },
-    {"nativeSetDesiredDisplayConfigSpecs",
-            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
-            (void*)nativeSetDesiredDisplayConfigSpecs },
-    {"nativeGetDesiredDisplayConfigSpecs",
-            "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;",
-            (void*)nativeGetDesiredDisplayConfigSpecs },
+    {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;",
+            (void*)nativeGetDisplayModes },
+    {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I",
+            (void*)nativeGetActiveDisplayMode },
+    {"nativeSetDesiredDisplayModeSpecs",
+            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z",
+            (void*)nativeSetDesiredDisplayModeSpecs },
+    {"nativeGetDesiredDisplayModeSpecs",
+            "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;",
+            (void*)nativeGetDesiredDisplayModeSpecs },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
     {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1838,8 +1824,6 @@
             (void*)nativeGetProtectedContentSupport },
     {"nativeDeferTransactionUntil", "(JJJJ)V",
             (void*)nativeDeferTransactionUntil },
-    {"nativeReparentChildren", "(JJJ)V",
-            (void*)nativeReparentChildren } ,
     {"nativeReparent", "(JJJ)V",
             (void*)nativeReparent },
     {"nativeCaptureDisplay",
@@ -1914,19 +1898,19 @@
             GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
                             "Landroid/hardware/display/DeviceProductInfo;");
 
-    jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
-    gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
-    gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V");
-    gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I");
-    gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I");
-    gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F");
-    gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F");
-    gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F");
-    gDisplayConfigClassInfo.appVsyncOffsetNanos =
-            GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
-    gDisplayConfigClassInfo.presentationDeadlineNanos =
-            GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
-    gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I");
+    jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
+    gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
+    gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V");
+    gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I");
+    gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I");
+    gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
+    gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F");
+    gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F");
+    gDisplayModeClassInfo.appVsyncOffsetNanos =
+            GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J");
+    gDisplayModeClassInfo.presentationDeadlineNanos =
+            GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J");
+    gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I");
 
     jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
     gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
@@ -2017,24 +2001,23 @@
     gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
             "Landroid/view/SurfaceControl$CieXyz;");
 
-    jclass desiredDisplayConfigSpecsClazz =
-            FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs");
-    gDesiredDisplayConfigSpecsClassInfo.clazz =
-            MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
-    gDesiredDisplayConfigSpecsClassInfo.ctor =
-            GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
-    gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
-    gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z");
-    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
-    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F");
-    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F");
-    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F");
+    jclass DesiredDisplayModeSpecsClazz =
+            FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs");
+    gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz);
+    gDesiredDisplayModeSpecsClassInfo.ctor =
+            GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
+    gDesiredDisplayModeSpecsClassInfo.defaultMode =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I");
+    gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z");
+    gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F");
+    gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F");
+    gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F");
+    gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
 
     jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs");
     gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index 7249238..d8446ca 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -20,6 +20,10 @@
 
 namespace android {
 
+static jboolean KernelCpuTotalBpfMapReader_isSupported(JNIEnv *, jobject) {
+    return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
+}
+
 static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) {
     jclass callbackClass = env->GetObjectClass(callback);
     jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V");
@@ -47,6 +51,7 @@
 static const JNINativeMethod methods[] = {
         {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z",
          (void *)KernelCpuTotalBpfMapReader_read},
+        {"isSupported", "()Z", (void *)KernelCpuTotalBpfMapReader_isSupported},
 };
 
 int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3d1c38d..e801725 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2506,6 +2506,36 @@
 #endif
 }
 
+static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+  int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  if (level < 0) {
+    ALOGE("Failed to get memory tag level: %s", strerror(errno));
+    return 0;
+  } else if (!(level & PR_TAGGED_ADDR_ENABLE)) {
+    return 0;
+  }
+  // TBI is only possible on non-MTE hardware.
+  if (!mte_supported()) {
+    return MEMORY_TAG_LEVEL_TBI;
+  }
+
+  switch (level & PR_MTE_TCF_MASK) {
+    case PR_MTE_TCF_NONE:
+      return 0;
+    case PR_MTE_TCF_SYNC:
+      return MEMORY_TAG_LEVEL_SYNC;
+    case PR_MTE_TCF_ASYNC:
+      return MEMORY_TAG_LEVEL_ASYNC;
+    default:
+      ALOGE("Unknown memory tagging level: %i", level);
+      return 0;
+  }
+#else // defined(__aarch64__)
+  return 0;
+#endif // defined(__aarch64__)
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2545,6 +2575,8 @@
          (void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging},
         {"nativeSupportsTaggedPointers", "()Z",
          (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
+        {"nativeCurrentTaggingLevel", "()I",
+         (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index bb39ea8..5c6116a 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,4 +114,5 @@
         optional bool native_heap_zero_init = 21;
     }
     optional Detail detail = 17;
+    repeated string overlay_paths = 18;
 }
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
index 6794c8c..0041f19 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -63,6 +63,9 @@
         // higher-scoring network will not go into the background immediately,
         // but will linger and go into the background after the linger timeout.
         TYPE_BACKGROUND_REQUEST = 5;
+        // Like TRACK_DEFAULT, but tracks the system default network, instead of
+        // the default network of the calling application.
+        TYPE_TRACK_SYSTEM_DEFAULT = 6;
     }
     // The type of the request. This is only used by the system and is always
     // NONE elsewhere.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 08c907b..856657a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,10 +690,6 @@
     <!-- Made protected in S (was added in R) -->
     <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
 
-    <!-- Added in S -->
-    <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
-    <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -2702,11 +2698,11 @@
          The app can check whether it has this authorization by calling
          {@link android.provider.Settings#canDrawOverlays
          Settings.canDrawOverlays()}.
-         <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development -->
+         <p>Protection level: signature|appop|installer|appPredictor|pre23|development -->
     <permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
         android:label="@string/permlab_systemAlertWindow"
         android:description="@string/permdesc_systemAlertWindow"
-        android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" />
+        android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
 
     <!-- @SystemApi @hide Allows an application to create windows using the type
          {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -5431,7 +5427,7 @@
     <permission android:name="android.permission.INPUT_CONSUMER"
                 android:protectionLevel="signature" />
 
-    <!-- @hide Allows an application to control the system's device state managed by the
+    <!-- @hide @TestApi Allows an application to control the system's device state managed by the
          {@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
          devices this would grant access to toggle between the folded and unfolded states. -->
     <permission android:name="android.permission.CONTROL_DEVICE_STATE"
@@ -5451,6 +5447,11 @@
     <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
                 android:protectionLevel="signature" />
 
+    <!-- Allows managing the Game Mode
+     @hide Used internally. -->
+    <permission android:name="android.permission.MANAGE_GAME_MODE"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 1a574fe..ad68054 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -23,7 +23,7 @@
     <ripple android:color="?attr/colorControlHighlight">
         <item>
             <shape android:shape="rectangle">
-                <corners android:radius="?attr/buttonCornerRadius" />
+                <corners android:radius="@dimen/notification_action_button_radius" />
                 <padding android:left="@dimen/button_padding_horizontal_material"
                          android:top="@dimen/button_padding_vertical_material"
                          android:right="@dimen/button_padding_horizontal_material"
diff --git a/core/res/res/drawable/ic_call_answer.xml b/core/res/res/drawable/ic_call_answer.xml
new file mode 100644
index 0000000..77c0ad1
--- /dev/null
+++ b/core/res/res/drawable/ic_call_answer.xml
@@ -0,0 +1,34 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20"
+    android:tint="?android:attr/colorControlNormal"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.0168,3.3333L6.5751,5.575L4.6668,7.4916C3.8751,5.7166 3.6001,4.1916
+            3.4834,3.3333H6.0168ZM14.4251,13.375L16.6668,13.9416V16.5166C15.8084,16.4 14.2668,16.125
+            12.4834,15.325L14.4251,13.375ZM6.3418,1.6666H2.5668C2.0918,1.6666 1.7001,2.0666
+            1.7334,2.5416C2.4834,12.875 11.7668,18.275 17.5168,18.275C17.9668,18.275 18.3334,17.9
+            18.3334,17.4416V13.6166C18.3334,13.0416 17.9418,12.5416
+            17.3834,12.4083L14.5918,11.7083C14.2251,11.6166 13.7584,11.6833
+            13.4084,12.0333L10.9251,14.5166C8.6751,13.1833 6.7918,11.3
+            5.4668,9.0416L7.9168,6.5916C8.2251,6.2833 8.3501,5.8333
+            8.2418,5.4083L7.5584,2.6166C7.4168,2.0583 6.9168,1.6666 6.3418,1.6666Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_call_decline.xml b/core/res/res/drawable/ic_call_decline.xml
new file mode 100644
index 0000000..a5ee8f4
--- /dev/null
+++ b/core/res/res/drawable/ic_call_decline.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20"
+    android:tint="?android:attr/colorControlNormal"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5.2834,9.3083V10.7833L4.1084,11.4916L3.1334,10.5166C3.8084,10.0416
+            4.5251,9.6333 5.2834,9.3083ZM14.75,9.325C15.4917,9.65 16.2084,10.05
+            16.875,10.525L15.925,11.475L14.75,10.7666V9.325ZM9.975,6.6666C6.8584,6.6666 3.725,7.7333
+            1.1751,9.9416C0.8084,10.2583 0.9667,10.7166 1.1501,10.9L3.2917,13.0416C3.4917,13.2333
+            3.7417,13.3333 4.0001,13.3333C4.175,13.3333 4.35,13.2833
+            4.5084,13.1916L6.4667,12.0166C6.725,11.8583 6.95,11.5583 6.95,11.1666V8.3833C7.95,8.125
+            8.975,8 10.0084,8C11.0417,8 12.075,8.1333 13.0834,8.3916V11.1416C13.0834,11.4916
+            13.2667,11.8166 13.5667,11.9916L15.525,13.1666C15.6834,13.2583 15.8584,13.3083
+            16.0334,13.3083C16.2917,13.3083 16.5417,13.2083
+            16.7334,13.0166L18.85,10.9C19.1167,10.6333 19.1167,10.1916 18.825,9.9416C16.3334,7.7833
+            13.1667,6.6666 9.975,6.6666Z"/>
+</vector>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index a6b7b38..cd1f1ab 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -22,6 +22,7 @@
     android:layout_height="match_parent"
     android:layout_marginStart="12dp"
     android:layout_weight="1"
+    android:drawablePadding="6dp"
     android:gravity="center"
     android:textColor="@color/notification_default_color"
     android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml
new file mode 100644
index 0000000..b018676
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_header.xml
@@ -0,0 +1,166 @@
+<?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
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/conversation_header"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:paddingTop="16dp"
+    >
+
+    <TextView
+        android:id="@+id/conversation_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+        android:textSize="16sp"
+        android:singleLine="true"
+        android:layout_weight="1"
+        />
+
+    <TextView
+        android:id="@+id/app_name_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+        android:text="@string/notification_header_divider_symbol"
+        android:layout_gravity="center"
+        android:paddingTop="1sp"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <!-- App Name -->
+    <com.android.internal.widget.ObservableTextView
+        android:id="@+id/app_name_text"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+        android:paddingTop="1sp"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <TextView
+        android:id="@+id/time_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+        android:text="@string/notification_header_divider_symbol"
+        android:layout_gravity="center"
+        android:paddingTop="1sp"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <DateTimeView
+        android:id="@+id/time"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:paddingTop="1sp"
+        android:showRelative="true"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <ViewStub
+        android:id="@+id/chronometer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:layout="@layout/notification_template_part_chronometer"
+        android:visibility="gone"
+        />
+
+    <ImageView
+        android:id="@+id/verification_icon"
+        android:layout_width="@dimen/notification_badge_size"
+        android:layout_height="@dimen/notification_badge_size"
+        android:layout_gravity="center"
+        android:layout_marginStart="4dp"
+        android:contentDescription="@string/notification_alerted_content_description"
+        android:paddingTop="2dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_notifications_alerted"
+        android:visibility="gone"
+        />
+
+    <TextView
+        android:id="@+id/verification_text"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+        android:paddingTop="1sp"
+        android:showRelative="true"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <ImageButton
+        android:id="@+id/feedback"
+        android:layout_width="@dimen/notification_feedback_size"
+        android:layout_height="@dimen/notification_feedback_size"
+        android:layout_gravity="center"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:background="?android:selectableItemBackgroundBorderless"
+        android:contentDescription="@string/notification_feedback_indicator"
+        android:paddingTop="2dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_feedback_indicator"
+        android:visibility="gone"
+        />
+
+    <ImageView
+        android:id="@+id/profile_badge"
+        android:layout_width="@dimen/notification_badge_size"
+        android:layout_height="@dimen/notification_badge_size"
+        android:layout_gravity="center"
+        android:layout_marginStart="4dp"
+        android:paddingTop="2dp"
+        android:scaleType="fitCenter"
+        android:visibility="gone"
+        android:contentDescription="@string/notification_work_profile_content_description"
+        />
+
+    <ImageView
+        android:id="@+id/alerted_icon"
+        android:layout_width="@dimen/notification_alerted_size"
+        android:layout_height="@dimen/notification_alerted_size"
+        android:layout_gravity="center"
+        android:layout_marginStart="4dp"
+        android:contentDescription="@string/notification_alerted_content_description"
+        android:paddingTop="2dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_notifications_alerted"
+        android:visibility="gone"
+        />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_conversation_icon_container.xml b/core/res/res/layout/notification_template_conversation_icon_container.xml
new file mode 100644
index 0000000..e9ec7ce
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_icon_container.xml
@@ -0,0 +1,97 @@
+<?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
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/conversation_icon_container"
+    android:layout_width="@dimen/conversation_content_start"
+    android:layout_height="wrap_content"
+    android:gravity="start|top"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp"
+    android:importantForAccessibility="no"
+    >
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:layout_gravity="top|center_horizontal"
+        >
+
+        <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
+        <com.android.internal.widget.CachingIconView
+            android:id="@+id/conversation_icon"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:scaleType="centerCrop"
+            android:importantForAccessibility="no"
+            />
+
+        <ViewStub
+            android:layout="@layout/conversation_face_pile_layout"
+            android:layout_width="@dimen/conversation_avatar_size"
+            android:layout_height="@dimen/conversation_avatar_size"
+            android:id="@+id/conversation_face_pile"
+            />
+
+        <FrameLayout
+            android:id="@+id/conversation_icon_badge"
+            android:layout_width="@dimen/conversation_icon_size_badged"
+            android:layout_height="@dimen/conversation_icon_size_badged"
+            android:layout_marginLeft="@dimen/conversation_badge_side_margin"
+            android:layout_marginTop="@dimen/conversation_badge_side_margin"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            >
+
+            <com.android.internal.widget.CachingIconView
+                android:id="@+id/conversation_icon_badge_bg"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_gravity="center"
+                android:src="@drawable/conversation_badge_background"
+                android:forceHasOverlappingRendering="false"
+                android:scaleType="center"
+                />
+
+            <com.android.internal.widget.CachingIconView
+                android:id="@+id/icon"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_margin="4dp"
+                android:layout_gravity="center"
+                android:forceHasOverlappingRendering="false"
+                />
+
+            <com.android.internal.widget.CachingIconView
+                android:id="@+id/conversation_icon_badge_ring"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:src="@drawable/conversation_badge_ring"
+                android:visibility="gone"
+                android:forceHasOverlappingRendering="false"
+                android:clipToPadding="false"
+                android:scaleType="center"
+                />
+        </FrameLayout>
+    </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
new file mode 100644
index 0000000..471d874
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -0,0 +1,92 @@
+<?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
+  -->
+<com.android.internal.widget.CallLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:clipChildren="false"
+    android:tag="call"
+    android:theme="@style/Theme.DeviceDefault.Notification"
+    >
+
+    <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+    <include layout="@layout/notification_template_conversation_icon_container" />
+
+    <LinearLayout
+        android:id="@+id/notification_action_list_margin_target"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/notification_action_list_height"
+        android:orientation="vertical"
+        >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="top"
+            android:orientation="horizontal"
+            >
+
+            <LinearLayout
+                android:id="@+id/notification_main_column"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_marginStart="@dimen/conversation_content_start"
+                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:orientation="vertical"
+                android:minHeight="68dp"
+                >
+
+                <include
+                    layout="@layout/notification_template_conversation_header"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    />
+
+                <include layout="@layout/notification_template_text" />
+
+                <include
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_progress_bar_height"
+                    android:layout_marginTop="@dimen/notification_progress_margin_top"
+                    layout="@layout/notification_template_progress"
+                    />
+            </LinearLayout>
+
+            <!-- TODO(b/179178086): remove padding from main column when this is visible -->
+            <com.android.internal.widget.NotificationExpandButton
+                android:id="@+id/expand_button"
+                android:layout_width="@dimen/notification_header_expand_icon_size"
+                android:layout_height="@dimen/notification_header_expand_icon_size"
+                android:layout_gravity="top|end"
+                android:contentDescription="@string/expand_button_content_description_collapsed"
+                android:paddingTop="@dimen/notification_expand_button_padding_top"
+                android:scaleType="center"
+                android:visibility="gone"
+                />
+
+        </LinearLayout>
+
+        <include layout="@layout/notification_material_action_list" />
+
+    </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index f9364d5..f3aa540 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -24,82 +24,7 @@
     android:theme="@style/Theme.DeviceDefault.Notification"
     >
 
-    <FrameLayout
-        android:id="@+id/conversation_icon_container"
-        android:layout_width="@dimen/conversation_content_start"
-        android:layout_height="wrap_content"
-        android:gravity="start|top"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:paddingTop="12dp"
-        android:paddingBottom="12dp"
-        android:importantForAccessibility="no"
-    >
-
-        <FrameLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:layout_gravity="top|center_horizontal"
-        >
-
-            <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
-            <com.android.internal.widget.CachingIconView
-                android:id="@+id/conversation_icon"
-                android:layout_width="@dimen/conversation_avatar_size"
-                android:layout_height="@dimen/conversation_avatar_size"
-                android:scaleType="centerCrop"
-                android:importantForAccessibility="no"
-            />
-
-            <ViewStub
-                android:layout="@layout/conversation_face_pile_layout"
-                android:layout_width="@dimen/conversation_avatar_size"
-                android:layout_height="@dimen/conversation_avatar_size"
-                android:id="@+id/conversation_face_pile"
-                />
-
-            <FrameLayout
-                android:id="@+id/conversation_icon_badge"
-                android:layout_width="@dimen/conversation_icon_size_badged"
-                android:layout_height="@dimen/conversation_icon_size_badged"
-                android:layout_marginLeft="@dimen/conversation_badge_side_margin"
-                android:layout_marginTop="@dimen/conversation_badge_side_margin"
-                android:clipChildren="false"
-                android:clipToPadding="false"
-            >
-                <com.android.internal.widget.CachingIconView
-                    android:id="@+id/conversation_icon_badge_bg"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:layout_gravity="center"
-                    android:src="@drawable/conversation_badge_background"
-                    android:forceHasOverlappingRendering="false"
-                    android:scaleType="center"
-                />
-                <com.android.internal.widget.CachingIconView
-                    android:id="@+id/icon"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:layout_margin="4dp"
-                    android:layout_gravity="center"
-                    android:forceHasOverlappingRendering="false"
-                />
-                <com.android.internal.widget.CachingIconView
-                    android:id="@+id/conversation_icon_badge_ring"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:src="@drawable/conversation_badge_ring"
-                    android:visibility="gone"
-                    android:forceHasOverlappingRendering="false"
-                    android:clipToPadding="false"
-                    android:scaleType="center"
-                />
-            </FrameLayout>
-        </FrameLayout>
-    </FrameLayout>
+    <include layout="@layout/notification_template_conversation_icon_container" />
 
     <!-- Wraps entire "expandable" notification -->
     <com.android.internal.widget.RemeasuringLinearLayout
@@ -132,161 +57,14 @@
 
                 <!-- Use layout_marginStart instead of paddingStart to work around strange
                      measurement behavior on lower display densities. -->
-                <LinearLayout
-                    android:id="@+id/conversation_header"
+                <include
+                    layout="@layout/notification_template_conversation_header"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:orientation="horizontal"
-                    android:paddingTop="16dp"
                     android:layout_marginBottom="2dp"
                     android:layout_marginStart="@dimen/conversation_content_start"
-                >
-                    <TextView
-                        android:id="@+id/conversation_text"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
-                        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
-                        android:textSize="16sp"
-                        android:singleLine="true"
-                        android:layout_weight="1"
-                        />
-
-                    <TextView
-                        android:id="@+id/app_name_divider"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:textAppearance="?attr/notificationHeaderTextAppearance"
-                        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-                        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
-                        android:text="@string/notification_header_divider_symbol"
-                        android:layout_gravity="center"
-                        android:paddingTop="1sp"
-                        android:singleLine="true"
-                        android:visibility="gone"
                     />
 
-                    <!-- App Name -->
-                    <com.android.internal.widget.ObservableTextView
-                        android:id="@+id/app_name_text"
-                        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center"
-                        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-                        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
-                        android:paddingTop="1sp"
-                        android:singleLine="true"
-                        android:visibility="gone"
-                    />
-
-                    <TextView
-                        android:id="@+id/time_divider"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:textAppearance="?attr/notificationHeaderTextAppearance"
-                        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-                        android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
-                        android:text="@string/notification_header_divider_symbol"
-                        android:layout_gravity="center"
-                        android:paddingTop="1sp"
-                        android:singleLine="true"
-                        android:visibility="gone"
-                    />
-
-                    <DateTimeView
-                        android:id="@+id/time"
-                        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center"
-                        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-                        android:paddingTop="1sp"
-                        android:showRelative="true"
-                        android:singleLine="true"
-                        android:visibility="gone"
-                    />
-
-                    <ImageButton
-                        android:id="@+id/feedback"
-                        android:layout_width="@dimen/notification_feedback_size"
-                        android:layout_height="@dimen/notification_feedback_size"
-                        android:layout_gravity="center"
-                        android:layout_marginStart="@dimen/notification_header_separating_margin"
-                        android:background="?android:selectableItemBackgroundBorderless"
-                        android:contentDescription="@string/notification_feedback_indicator"
-                        android:paddingTop="2dp"
-                        android:scaleType="fitCenter"
-                        android:src="@drawable/ic_feedback_indicator"
-                        android:visibility="gone"
-                        />
-
-                    <ImageView
-                        android:id="@+id/profile_badge"
-                        android:layout_width="@dimen/notification_badge_size"
-                        android:layout_height="@dimen/notification_badge_size"
-                        android:layout_gravity="center"
-                        android:layout_marginStart="4dp"
-                        android:paddingTop="2dp"
-                        android:scaleType="fitCenter"
-                        android:visibility="gone"
-                        android:contentDescription="@string/notification_work_profile_content_description"
-                        />
-
-                    <ImageView
-                        android:id="@+id/alerted_icon"
-                        android:layout_width="@dimen/notification_alerted_size"
-                        android:layout_height="@dimen/notification_alerted_size"
-                        android:layout_gravity="center"
-                        android:layout_marginStart="4dp"
-                        android:contentDescription="@string/notification_alerted_content_description"
-                        android:paddingTop="2dp"
-                        android:scaleType="fitCenter"
-                        android:src="@drawable/ic_notifications_alerted"
-                        android:visibility="gone"
-                        />
-
-                    <LinearLayout
-                        android:id="@+id/app_ops"
-                        android:layout_height="wrap_content"
-                        android:layout_width="wrap_content"
-                        android:paddingTop="3dp"
-                        android:layout_marginStart="2dp"
-                        android:background="?android:selectableItemBackgroundBorderless"
-                        android:orientation="horizontal" >
-                        <ImageView
-                            android:layout_marginStart="4dp"
-                            android:id="@+id/camera"
-                            android:layout_width="?attr/notificationHeaderIconSize"
-                            android:layout_height="?attr/notificationHeaderIconSize"
-                            android:src="@drawable/ic_camera"
-                            android:visibility="gone"
-                            android:focusable="false"
-                            android:contentDescription="@string/notification_appops_camera_active"
-                            />
-                        <ImageView
-                            android:id="@+id/mic"
-                            android:layout_width="?attr/notificationHeaderIconSize"
-                            android:layout_height="?attr/notificationHeaderIconSize"
-                            android:src="@drawable/ic_mic"
-                            android:layout_marginStart="4dp"
-                            android:visibility="gone"
-                            android:focusable="false"
-                            android:contentDescription="@string/notification_appops_microphone_active"
-                            />
-                        <ImageView
-                            android:id="@+id/overlay"
-                            android:layout_width="?attr/notificationHeaderIconSize"
-                            android:layout_height="?attr/notificationHeaderIconSize"
-                            android:src="@drawable/ic_alert_window_layer"
-                            android:layout_marginStart="4dp"
-                            android:visibility="gone"
-                            android:focusable="false"
-                            android:contentDescription="@string/notification_appops_overlay_active"
-                            />
-                    </LinearLayout>
-                </LinearLayout>
-
                 <!-- Messages -->
                 <com.android.internal.widget.MessagingLinearLayout
                     android:id="@+id/notification_messaging"
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index c5ffbea..e4ebc76 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
 -->
 
 <Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+    android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginEnd="4dp"
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index 7cda03f..7656dd5 100644
--- a/core/res/res/layout/notification_top_line_views.xml
+++ b/core/res/res/layout/notification_top_line_views.xml
@@ -26,7 +26,7 @@
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:singleLine="true"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:visibility="?attr/notificationHeaderAppNameVisibility"
         />
 
@@ -34,7 +34,7 @@
         android:id="@+id/header_text_secondary_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:text="@string/notification_header_divider_symbol"
@@ -45,7 +45,7 @@
         android:id="@+id/header_text_secondary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:visibility="gone"
@@ -56,7 +56,7 @@
         android:id="@+id/header_text_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:text="@string/notification_header_divider_symbol"
@@ -67,7 +67,7 @@
         android:id="@+id/header_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:visibility="gone"
@@ -78,7 +78,7 @@
         android:id="@+id/time_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/notificationHeaderTextAppearance"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:text="@string/notification_header_divider_symbol"
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 39e1bbb..9e1692f 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -22,7 +22,8 @@
     android:importantForAccessibility="noHideDescendants"
     android:clickable="true">
 
-    <ImageView android:id="@+id/work_widget_app_icon"
+    <com.android.internal.widget.DisableImageView
+        android:id="@+id/work_widget_app_icon"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index eb86709..586c99d7 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3251,6 +3251,16 @@
              a value of 'true' will not override any 'false' value in its parent chain nor will
              it prevent any 'false' in any of its children. -->
         <attr name="forceDarkAllowed" format="boolean" />
+
+        <!-- <p>Whether the View's Outline should be used to clip the contents of the View.
+             <p>Only a single non-rectangular clip can be applied on a View at any time. Circular
+             clips from a
+             {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float,
+             float)} circular reveal animation take priority over Outline clipping, and child
+             Outline clipping takes priority over Outline clipping done by a parent.
+             <p>Note that this flag will only be respected if the View's Outline returns true from
+             {@link android.graphics.Outline#canClip()}. -->
+        <attr name="clipToOutline" format="boolean" />
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -7969,9 +7979,17 @@
         <!-- A class name in the AppWidget's package to be launched to configure.
              If not supplied, then no activity will be launched. -->
         <attr name="configure" format="string" />
-        <!-- A preview of what the AppWidget will look like after it's configured.
-              If not supplied, the AppWidget's icon will be used. -->
+        <!-- A preview, in a drawable resource id, of what the AppWidget will look like after it's
+             configured.
+             If not supplied, the AppWidget's icon will be used. -->
         <attr name="previewImage" format="reference" />
+        <!-- The layout resource id of a preview of what the AppWidget will look like after it's
+             configured.
+             Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+             system themes, display sizes & density etc.
+             If supplied, this will take precedence over the previewImage on supported widget hosts.
+             Otherwise, previewImage will be used. -->
+        <attr name="previewLayout" format="reference" />
         <!-- The view id of the AppWidget subview which should be auto-advanced.
              by the widget's host. -->
         <attr name="autoAdvanceViewId" format="reference" />
@@ -8500,6 +8518,9 @@
              interaction requests from an Activity. This flag is new in
              {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
         <attr name="supportsLocalInteraction" format="boolean" />
+        <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
+             @hide @SystemApi -->
+        <attr name="hotwordDetectionService" format="string" />
     </declare-styleable>
 
     <!-- Use <code>voice-enrollment-application</code>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 20a5d37..5546621 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -153,6 +153,11 @@
 
     <color name="notification_action_list_background_color">@null</color>
 
+    <!-- The color of the Decline and Hang Up actions on a CallStyle notification -->
+    <color name="call_notification_decline_color">#d93025</color>
+    <!-- The color of the Answer action on a CallStyle notification -->
+    <color name="call_notification_answer_color">#1e8e3e</color>
+
     <!-- Keyguard colors -->
     <color name="keyguard_avatar_frame_color">#ffffffff</color>
     <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index c8cccc4..f723426 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -48,6 +48,8 @@
     <color name="text_color_secondary_device_default_dark">@color/system_main_200</color>
     <color name="text_color_tertiary_device_default_light">@color/system_main_500</color>
     <color name="text_color_tertiary_device_default_dark">@color/system_main_400</color>
+    <color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color>
+    <color name="foreground_device_default_dark">@color/text_color_primary_device_default_dark</color>
 
     <!-- Error color -->
     <color name="error_color_device_default_dark">@color/error_color_material_dark</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3790aa4..beae935 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3477,6 +3477,11 @@
     <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
     <integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
 
+    <!-- If true, jobs from background user will be restricted -->
+    <bool name="config_jobSchedulerRestrictBackgroundUser">false</bool>
+    <!-- The length of grace period after user becomes background user -->
+    <integer name="config_jobSchedulerUserGracePeriod">60000</integer>
+
     <!-- If true, all guest users created on the device will be ephemeral. -->
     <bool name="config_guestUserEphemeral">false</bool>
 
@@ -4710,4 +4715,7 @@
 
     <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
     <bool name="config_enableOneHandedKeyguard">false</bool>
+
+    <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
+    <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cb16af5..3b6e41f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,9 +236,20 @@
          value is calculated in ConversationLayout#updateActionListPadding() -->
     <dimen name="notification_actions_padding_start">36dp</dimen>
 
+    <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle
+         notification actions.
+         this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) -->
+    <dimen name="call_notification_collapsible_indent">64dp</dimen>
+
     <!-- The size of icons for visual actions in the notification_material_action_list -->
     <dimen name="notification_actions_icon_size">48dp</dimen>
 
+    <!-- The size of icons for visual actions in the notification_material_action_list -->
+    <dimen name="notification_actions_icon_drawable_size">20dp</dimen>
+
+    <!-- The corner radius if the emphasized action buttons in a notification -->
+    <dimen name="notification_action_button_radius">8dp</dimen>
+
     <!-- Size of the stroke with for the emphasized notification button style -->
     <dimen name="emphasized_button_stroke_width">1dp</dimen>
 
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a4c7293..ab4e0f3 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -102,6 +102,10 @@
   <item type="id" name="selection_end_handle" />
   <item type="id" name="insertion_handle" />
   <item type="id" name="floating_toolbar_menu_item_image_button" />
+  <item type="id" name="camera" />
+  <item type="id" name="mic" />
+  <item type="id" name="overlay" />
+  <item type="id" name="app_ops" />
 
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
   <item type="id" name="accessibilityActionShowOnScreen" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a7e8f2a..d3f3ebd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,6 +3059,10 @@
     <public name="hand_second" />
     <public name="memtagMode" />
     <public name="nativeHeapZeroInit" />
+    <!-- @hide @SystemApi -->
+    <public name="hotwordDetectionService" />
+    <public name="previewLayout" />
+    <public name="clipToOutline" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 98b36c5..af5e406 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1585,6 +1585,8 @@
 
     <!-- Template to be used to name enrolled fingerprints by default. -->
     <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+    <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
+    <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
 
     <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="fingerprint_error_vendor">
@@ -4530,9 +4532,8 @@
      shown in the warning dialog about the accessibility shortcut. -->
     <string name="color_correction_feature_name">Color Correction</string>
 
-    <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
-    <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
-    <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string>
+    <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+    <string name="reduce_bright_colors_feature_name">Reduce Brightness</string>
 
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
@@ -5038,6 +5039,18 @@
     <!-- Tempalate for Notification.MessagingStyle to join a conversation name with the name of the sender of a message, to make a notification title [CHAR LIMIT=NONE] -->
     <string name="notification_messaging_title_template"><xliff:g id="conversation_title" example="Tasty Treat Team">%1$s</xliff:g>: <xliff:g id="sender_name" example="Adrian Baker">%2$s</xliff:g></string>
 
+    <!-- Action text to be displayed for the "answer" action of an incoming call [CHAR LIMIT=13] -->
+    <string name="call_notification_answer_action">Answer</string>
+    <!-- Action text to be displayed for the "decline" action of an incoming call [CHAR LIMIT=13] -->
+    <string name="call_notification_decline_action">Decline</string>
+    <!-- Action text to be displayed for the "hang up" action of an ongoing call [CHAR LIMIT=13] -->
+    <string name="call_notification_hang_up_action">Hang Up</string>
+    <!-- Default notification text to be displayed in incoming call notifications [CHAR LIMIT=40] -->
+    <string name="call_notification_incoming_text">Incoming call</string>
+    <!-- Default notification text to be displayed in ongoing call notifications [CHAR LIMIT=40] -->
+    <string name="call_notification_ongoing_text">Ongoing call</string>
+    <!-- Default notification text to be displayed in screening call notifications [CHAR LIMIT=40] -->
+    <string name="call_notification_screening_text">Screening an incoming call</string>
 
     <!-- Label describing the number of selected items [CHAR LIMIT=48] -->
     <plurals name="selected_count">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 80163b1..4109d4c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2488,6 +2488,7 @@
   <java-symbol type="string" name="fingerprint_error_lockout" />
   <java-symbol type="string" name="fingerprint_error_lockout_permanent" />
   <java-symbol type="string" name="fingerprint_name_template" />
+  <java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
   <java-symbol type="string" name="fingerprint_authenticated" />
   <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
   <java-symbol type="string" name="fingerprint_error_hw_not_present" />
@@ -2646,6 +2647,7 @@
   <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
   <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
   <java-symbol type="attr" name="colorProgressBackgroundNormal" />
+  <java-symbol type="bool" name="config_allow_pin_storage_for_unattended_reboot" />
 
   <java-symbol type="layout" name="simple_account_item" />
   <java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
@@ -2679,6 +2681,8 @@
 
   <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
   <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
+  <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
+  <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
 
   <java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
 
@@ -3085,8 +3089,26 @@
 
   <!-- TV Remote Service package -->
   <java-symbol type="string" name="config_tvRemoteServicePackage" />
+
+  <!-- Notifications: MessagingStyle -->
   <java-symbol type="string" name="notification_messaging_title_template" />
 
+  <!-- Notifications: CallStyle -->
+  <java-symbol type="layout" name="notification_template_material_call" />
+  <java-symbol type="string" name="call_notification_answer_action" />
+  <java-symbol type="string" name="call_notification_decline_action" />
+  <java-symbol type="string" name="call_notification_hang_up_action" />
+  <java-symbol type="string" name="call_notification_incoming_text" />
+  <java-symbol type="string" name="call_notification_ongoing_text" />
+  <java-symbol type="string" name="call_notification_screening_text" />
+  <java-symbol type="color" name="call_notification_decline_color"/>
+  <java-symbol type="color" name="call_notification_answer_color"/>
+  <java-symbol type="dimen" name="call_notification_collapsible_indent"/>
+  <java-symbol type="drawable" name="ic_call_answer" />
+  <java-symbol type="drawable" name="ic_call_decline" />
+  <java-symbol type="id" name="verification_icon" />
+  <java-symbol type="id" name="verification_text" />
+
   <!-- Notification handler / dashboard package -->
   <java-symbol type="string" name="config_notificationHandlerPackage" />
 
@@ -3412,6 +3434,7 @@
   <java-symbol type="dimen" name="notification_media_image_max_width"/>
   <java-symbol type="dimen" name="notification_media_image_max_height"/>
   <java-symbol type="dimen" name="notification_right_icon_size"/>
+  <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
 
@@ -4175,6 +4198,4 @@
   <java-symbol type="bool" name="config_telephony5gNonStandalone" />
 
   <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
-
-  <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee17f6f..ce4ee87 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -222,6 +222,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -236,6 +238,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -267,6 +271,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -300,6 +306,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -332,6 +340,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -381,6 +391,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -405,6 +417,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -435,6 +449,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -466,6 +482,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -513,6 +531,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -545,6 +565,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -575,6 +597,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -607,6 +631,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -638,6 +664,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -669,6 +697,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -700,6 +730,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -731,6 +763,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -766,6 +800,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Text styles -->
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -798,6 +834,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -827,6 +865,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1012,6 +1052,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
         <item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
         <item name="panelColorBackground">?attr/colorBackgroundFloating</item>
     </style>
@@ -1027,6 +1069,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1057,6 +1101,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1088,6 +1134,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1121,6 +1169,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1153,6 +1203,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1204,6 +1256,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1227,6 +1281,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1260,6 +1316,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1294,6 +1352,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1329,6 +1389,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1346,6 +1408,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1362,6 +1426,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1397,6 +1463,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1430,6 +1498,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1462,6 +1532,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1493,6 +1565,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1524,6 +1598,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1553,6 +1629,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1672,6 +1750,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1702,6 +1782,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -1742,6 +1824,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1775,6 +1859,8 @@
         <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
         <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
         <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+        <item name="colorForeground">@color/foreground_device_default_light</item>
+        <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index da7304e..48b58c6 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -184,7 +184,8 @@
         final IBinder token = windowContext.getWindowContextToken();
 
         final IBinder existingToken = new Binder();
-        mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
+        mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(),
+                null /* options */);
 
         final WindowManager.LayoutParams params =
                 new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
index cfcfcc8..ece37f8 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
@@ -201,6 +201,18 @@
         assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2));
     }
 
+    @Test
+    public void testDeepHashCode_differentKeys() {
+        Bundle[] inputs = new Bundle[2];
+        for (int i = 0; i < 2; i++) {
+            Bundle b = new Bundle();
+            b.putString("key" + i, "value");
+            inputs[i] = b;
+        }
+        assertThat(BundleUtil.deepHashCode(inputs[0]))
+                .isNotEqualTo(BundleUtil.deepHashCode(inputs[1]));
+    }
+
     private static Bundle createThoroughBundle() {
         Bundle toy1 = new Bundle();
         toy1.putString("a", "a");
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 45adf83..46dbe0f 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -90,12 +90,12 @@
     @SmallTest
     public void testMultipleCallsWithIdenticalParametersCacheReference() {
         Resources resources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources);
 
         Resources newResources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(newResources);
         assertSame(resources, newResources);
@@ -104,14 +104,14 @@
     @SmallTest
     public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
         Resources resources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources);
 
         Configuration overrideConfig = new Configuration();
         overrideConfig.smallestScreenWidthDp = 200;
         Resources newResources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(newResources);
         assertNotSame(resources, newResources);
@@ -120,12 +120,12 @@
     @SmallTest
     public void testAddingASplitCreatesANewImpl() {
         Resources resources1 = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Resources resources2 = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null,
+                null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, null,
                 null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null,
                 null);
         assertNotNull(resources2);
@@ -137,12 +137,12 @@
     @SmallTest
     public void testUpdateConfigurationUpdatesAllAssetManagers() {
         Resources resources1 = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, null, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Resources resources2 = mResourcesManager.getResources(
-                null, APP_TWO_RES_DIR, null, null, null, null, null,
+                null, APP_TWO_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources2);
 
@@ -150,7 +150,7 @@
         final Configuration overrideConfig = new Configuration();
         overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
         Resources resources3 = mResourcesManager.getResources(
-                activity, APP_ONE_RES_DIR, null, null, null, null,
+                activity, APP_ONE_RES_DIR, null, null, null, null, null,
                 overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources3);
 
@@ -183,13 +183,13 @@
     public void testTwoActivitiesWithIdenticalParametersShareImpl() {
         Binder activity1 = new Binder();
         Resources resources1 = mResourcesManager.getResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, null, null,
+                activity1, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Binder activity2 = new Binder();
         Resources resources2 = mResourcesManager.getResources(
-                activity2, APP_ONE_RES_DIR, null, null, null, null, null,
+                activity2, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
@@ -204,7 +204,7 @@
     public void testThemesGetUpdatedWithNewImpl() {
         Binder activity1 = new Binder();
         Resources resources1 = mResourcesManager.createBaseTokenResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
@@ -237,15 +237,15 @@
         Configuration config1 = new Configuration();
         config1.densityDpi = 280;
         Resources resources1 = mResourcesManager.createBaseTokenResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
-                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+                activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY,
+                config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         // Create a Resources based on the Activity.
         Configuration config2 = new Configuration();
         config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
         Resources resources2 = mResourcesManager.getResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, null, config2,
+                activity1, APP_ONE_RES_DIR, null, null, null, null, null, config2,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources2);
 
@@ -286,8 +286,8 @@
         final Configuration overrideConfig = new Configuration();
         overrideConfig.densityDpi = originalOverrideDensity;
         final Resources resources = mResourcesManager.createBaseTokenResources(
-                token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */,
-                null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
+                token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */,
+                null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */,
                 null /* loaders */);
 
@@ -315,12 +315,12 @@
 
         // Create a base token resources that are based on the default display.
         Resources activityResources = mResourcesManager.createBaseTokenResources(
-                activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                activity, APP_ONE_RES_DIR, null, null, null,null, Display.DEFAULT_DISPLAY, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         // Create another resources that explicitly override the display of the base token above
         // and set it to DEFAULT_DISPLAY.
         Resources defaultDisplayResources = mResourcesManager.getResources(
-                activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
 
         assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
new file mode 100644
index 0000000..eae41e3
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -0,0 +1,245 @@
+/*
+ * 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.graphics;
+
+import static android.graphics.fonts.FontStyle.FONT_SLANT_ITALIC;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
+import static android.text.FontConfig.FontFamily.VARIANT_COMPACT;
+import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
+import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
+import android.text.FontConfig;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+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.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontListParserTest {
+
+    @Test
+    public void named() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void fallback() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en'>"
+                + "  <font>test.ttf</font>"
+                + "  <font fallbackFor='serif'>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", "serif")),
+                null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void compact() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en' variant='compact'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void elegant() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en' variant='elegant'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void styles() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font style='normal'>normal.ttf</font>"
+                + "  <font weight='100'>weight.ttf</font>"
+                + "  <font style='italic'>italic.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("normal.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("weight.ttf"), null,
+                                new FontStyle(100, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("italic.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
+                                0, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void variable() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font>test-VF.ttf"
+                + "    <axis tag='wdth' stylevalue='100' />"
+                + "    <axis tag='wght' stylevalue='200' />"
+                + "  </font>"
+                + "  <font>test-VF.ttf"
+                + "    <axis tag='wdth' stylevalue='400' />"
+                + "    <axis tag='wght' stylevalue='700' />"
+                + "  </font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test-VF.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "'wdth' 100.0,'wght' 200.0", null),
+                        new FontConfig.Font(new File("test-VF.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "'wdth' 400.0,'wght' 700.0", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void ttc() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font index='0'>test.ttc</font>"
+                + "  <font index='1'>test.ttc</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttc"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("test.ttc"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                1, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    private FontConfig.FontFamily readFamily(String xml)
+            throws IOException, XmlPullParserException {
+        StandardCharsets.UTF_8.name();
+        ByteArrayInputStream buffer = new ByteArrayInputStream(
+                xml.getBytes(StandardCharsets.UTF_8));
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(buffer, "UTF-8");
+        parser.nextTag();
+        return FontListParser.readFamily(parser, "", null);
+    }
+
+    private String writeFamily(FontConfig.FontFamily family) throws IOException {
+        TypedXmlSerializer out = Xml.newFastSerializer();
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        out.setOutput(buffer, "UTF-8");
+        out.startTag(null, "family");
+        FontListParser.writeFamily(out, family);
+        out.endTag(null, "family");
+        out.endDocument();
+        return buffer.toString("UTF-8");
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index be79355..219f741 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -42,7 +42,8 @@
 
     public void onClick(View v) {
         DisplayManager dm = getSystemService(DisplayManager.class);
-        dm.setTemporaryBrightness(0.0f);
+        final int displayId = getBaseContext().getDisplay().getDisplayId();
+        dm.setTemporaryBrightness(displayId, 0.0f);
         Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
     }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index a4284a0..47ce2d8 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -278,29 +278,29 @@
 
     @Test
     @UiThreadTest
-    public void setSetImeTemporarilyConsumesInput_recoveryToVisible() {
+    public void setSetImeConsumesInput_recoveryToVisible() {
         mTextView = new TextView(mActivity);
         mTextView.setCursorVisible(true);
         assertTrue(mTextView.isCursorVisible());
 
-        mTextView.setImeTemporarilyConsumesInput(true);
+        mTextView.setImeConsumesInput(true);
         assertFalse(mTextView.isCursorVisible());
 
-        mTextView.setImeTemporarilyConsumesInput(false);
+        mTextView.setImeConsumesInput(false);
         assertTrue(mTextView.isCursorVisible());
     }
 
     @Test
     @UiThreadTest
-    public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() {
+    public void setSetImeConsumesInput_recoveryToInvisible() {
         mTextView = new TextView(mActivity);
         mTextView.setCursorVisible(false);
         assertFalse(mTextView.isCursorVisible());
 
-        mTextView.setImeTemporarilyConsumesInput(true);
+        mTextView.setImeConsumesInput(true);
         assertFalse(mTextView.isCursorVisible());
 
-        mTextView.setImeTemporarilyConsumesInput(false);
+        mTextView.setImeConsumesInput(false);
         assertFalse(mTextView.isCursorVisible());
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 64906bb..e16d448 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -100,7 +100,7 @@
         final List<UsageStats> slices = new ArrayList<>();
         slices.add(packageStats);
         ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices);
-        when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString()))
+        when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), anyInt()))
                 .thenReturn(stats);
         Answer<Void> answer = new Answer<Void>() {
             @Override
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 143e07a..0fac4f7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -45,6 +45,7 @@
         BluetoothPowerCalculatorTest.class,
         BstatsCpuTimesValidationTest.class,
         CameraPowerCalculatorTest.class,
+        CpuPowerCalculatorTest.class,
         FlashlightPowerCalculatorTest.class,
         GnssPowerCalculatorTest.class,
         IdlePowerCalculatorTest.class,
@@ -65,6 +66,7 @@
         ScreenPowerCalculatorTest.class,
         SensorPowerCalculatorTest.class,
         SystemServicePowerCalculatorTest.class,
+        UserPowerCalculatorTest.class,
         VideoPowerCalculatorTest.class,
 
         com.android.internal.power.MeasuredEnergyStatsTest.class
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 2c71287..0ddc4c0 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -28,6 +28,7 @@
 import android.os.BatteryUsageStatsQuery;
 import android.os.SystemBatteryConsumer;
 import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
@@ -76,6 +77,26 @@
         return this;
     }
 
+    public BatteryUsageStatsRule setNumCpuClusters(int number) {
+        when(mPowerProfile.getNumCpuClusters()).thenReturn(number);
+        return this;
+    }
+
+    public BatteryUsageStatsRule setNumSpeedStepsInCpuCluster(int cluster, int speeds) {
+        when(mPowerProfile.getNumSpeedStepsInCpuCluster(cluster)).thenReturn(speeds);
+        return this;
+    }
+
+    public BatteryUsageStatsRule setAveragePowerForCpuCluster(int cluster, double value) {
+        when(mPowerProfile.getAveragePowerForCpuCluster(cluster)).thenReturn(value);
+        return this;
+    }
+
+    public BatteryUsageStatsRule setAveragePowerForCpuCore(int cluster, int step, double value) {
+        when(mPowerProfile.getAveragePowerForCpuCore(cluster, step)).thenReturn(value);
+        return this;
+    }
+
     public void setNetworkStats(NetworkStats networkStats) {
         mBatteryStats.setNetworkStats(networkStats);
     }
@@ -113,15 +134,21 @@
         mMockClocks.uptime = uptimeUs;
     }
 
-    void apply(PowerCalculator calculator) {
+    void apply(PowerCalculator... calculators) {
+        apply(BatteryUsageStatsQuery.DEFAULT, calculators);
+    }
+
+    void apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) {
         BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0);
         SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
         for (int i = 0; i < uidStats.size(); i++) {
             builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
         }
 
-        calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
-                BatteryUsageStatsQuery.DEFAULT, null);
+        for (PowerCalculator calculator : calculators) {
+            calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
+                    query);
+        }
 
         mBatteryUsageStats = builder.build();
     }
@@ -144,4 +171,13 @@
         }
         return null;
     }
+
+    public UserBatteryConsumer getUserBatteryConsumer(int userId) {
+        for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) {
+            if (ubc.getUserId() == userId) {
+                return ubc;
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
new file mode 100644
index 0000000..9cf0d37
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuPowerCalculatorTest {
+    private static final double PRECISION = 0.00001;
+
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272;
+
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+            .setNumCpuClusters(2)
+            .setNumSpeedStepsInCpuCluster(0, 2)
+            .setNumSpeedStepsInCpuCluster(1, 2)
+            .setAveragePowerForCpuCluster(0, 360)
+            .setAveragePowerForCpuCluster(1, 480)
+            .setAveragePowerForCpuCore(0, 0, 300)
+            .setAveragePowerForCpuCore(0, 1, 400)
+            .setAveragePowerForCpuCore(1, 0, 500)
+            .setAveragePowerForCpuCore(1, 1, 600);
+
+    private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
+            mock(KernelCpuSpeedReader.class),
+            mock(KernelCpuSpeedReader.class),
+    };
+
+    @Mock
+    private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
+    @Mock
+    private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mStatsRule.getBatteryStats()
+                .setUserInfoProvider(mMockUserInfoProvider)
+                .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
+                .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
+                .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
+                .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
+                .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
+                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+    }
+
+    @Test
+    public void testTimerBasedModel() {
+        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+        when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+        when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
+
+        when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+
+        // User/System CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            // User/system time in microseconds
+            callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
+            return null;
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
+
+        // Active CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+            callback.onUidCpuTime(APP_UID1, 1111L);
+            callback.onUidCpuTime(APP_UID2, 3333L);
+            return null;
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+
+        // Per-cluster CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+            return null;
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+
+        mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true);
+
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
+
+        CpuPowerCalculator calculator =
+                new CpuPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+        assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(3333);
+        assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(1.092233);
+        assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
+
+        UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(7777);
+        assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(2.672322);
+        assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
index 4068e2b..3f8f9e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -1 +1,4 @@
 include /BATTERY_STATS_OWNERS
+
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
new file mode 100644
index 0000000..6fa1d3b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UserPowerCalculatorTest {
+    public static final int USER1 = 0;
+    public static final int USER2 = 1625;
+
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272;
+    private static final int APP_UID3 = Process.FIRST_APPLICATION_UID + 314;
+
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+    @Test
+    public void testAllUsers() {
+        prepareUidBatteryConsumers();
+
+        UserPowerCalculator calculator = new UserPowerCalculator();
+
+        mStatsRule.apply(BatteryUsageStatsQuery.DEFAULT, calculator, new FakeAudioPowerCalculator(),
+                new FakeVideoPowerCalculator());
+
+        assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+
+        assertThat(mStatsRule.getUserBatteryConsumer(USER2)).isNull();
+
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(5555);
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(9999);
+    }
+
+    @Test
+    public void testSpecificUser() {
+        prepareUidBatteryConsumers();
+
+        UserPowerCalculator calculator = new UserPowerCalculator();
+
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().addUser(UserHandle.of(USER1)).build(),
+                calculator, new FakeAudioPowerCalculator(), new FakeVideoPowerCalculator());
+
+        assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID2))).isNull();
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(7070);
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+                .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(11110);
+
+        UserBatteryConsumer user2 = mStatsRule.getUserBatteryConsumer(USER2);
+        assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO))
+                .isEqualTo(15308);
+        assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO))
+                .isEqualTo(24196);
+
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID1))).isNull();
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))).isNull();
+        assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID3))).isNull();
+    }
+
+    private void prepareUidBatteryConsumers() {
+        prepareUidBatteryConsumer(USER1, APP_UID1, 1000, 2000, 3000, 4000);
+        prepareUidBatteryConsumer(USER2, APP_UID2, 2222, 3333, 4444, 5555);
+        prepareUidBatteryConsumer(USER1, APP_UID3, 3030, 4040, 5050, 6060);
+        prepareUidBatteryConsumer(USER2, APP_UID3, 4321, 5432, 6543, 7654);
+    }
+
+    private void prepareUidBatteryConsumer(int userId, int uid, long audioDuration1Ms,
+            long audioDuration2Ms, long videoDuration1Ms, long videoDuration2Ms) {
+        BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(UserHandle.getUid(userId, uid));
+
+        // Use "audio" and "video" to fake some power consumption. Could be any other type of usage.
+        uidStats.noteAudioTurnedOnLocked(0);
+        uidStats.noteAudioTurnedOffLocked(audioDuration1Ms);
+        uidStats.noteAudioTurnedOnLocked(1000000);
+        uidStats.noteAudioTurnedOffLocked(1000000 + audioDuration2Ms);
+
+        uidStats.noteVideoTurnedOnLocked(0);
+        uidStats.noteVideoTurnedOffLocked(videoDuration1Ms);
+        uidStats.noteVideoTurnedOnLocked(2000000);
+        uidStats.noteVideoTurnedOffLocked(2000000 + videoDuration2Ms);
+    }
+
+    private static class FakeAudioPowerCalculator extends PowerCalculator {
+        @Override
+        protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+                long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+            long durationMs = u.getAudioTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+            app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs / 1000);
+        }
+    }
+
+    private static class FakeVideoPowerCalculator extends PowerCalculator {
+        @Override
+        protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+                long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+            long durationMs = u.getVideoTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+            app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs / 1000);
+        }
+    }
+}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 36f01f9..e7fdfb8 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -17,8 +17,11 @@
 package android.hardware.devicestate;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
 
 import android.annotation.Nullable;
+import android.os.IBinder;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
@@ -30,6 +33,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -41,6 +45,9 @@
 @RunWith(JUnit4.class)
 @SmallTest
 public final class DeviceStateManagerGlobalTest {
+    private static final int DEFAULT_DEVICE_STATE = 0;
+    private static final int OTHER_DEVICE_STATE = 1;
+
     private TestDeviceStateManagerService mService;
     private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
 
@@ -52,7 +59,7 @@
 
     @Test
     public void registerListener() {
-        mService.setDeviceState(0);
+        mService.setBaseState(DEFAULT_DEVICE_STATE);
 
         TestDeviceStateListener listener1 = new TestDeviceStateListener();
         TestDeviceStateListener listener2 = new TestDeviceStateListener();
@@ -61,28 +68,58 @@
                 ConcurrentUtils.DIRECT_EXECUTOR);
         mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
                 ConcurrentUtils.DIRECT_EXECUTOR);
-        assertEquals(0, listener1.getLastReportedState().intValue());
-        assertEquals(0, listener2.getLastReportedState().intValue());
+        assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
+        assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
 
-        mService.setDeviceState(1);
-        assertEquals(1, listener1.getLastReportedState().intValue());
-        assertEquals(1, listener2.getLastReportedState().intValue());
+        mService.setBaseState(OTHER_DEVICE_STATE);
+        assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
+        assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
     }
 
     @Test
     public void unregisterListener() {
-        mService.setDeviceState(0);
+        mService.setBaseState(DEFAULT_DEVICE_STATE);
 
         TestDeviceStateListener listener = new TestDeviceStateListener();
 
         mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
                 ConcurrentUtils.DIRECT_EXECUTOR);
-        assertEquals(0, listener.getLastReportedState().intValue());
+        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
 
         mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
 
-        mService.setDeviceState(1);
-        assertEquals(0, listener.getLastReportedState().intValue());
+        mService.setBaseState(OTHER_DEVICE_STATE);
+        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+    }
+
+    @Test
+    public void submittingRequestRegisteredCallback() {
+        assertTrue(mService.mCallbacks.isEmpty());
+
+        DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+        mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+        assertFalse(mService.mCallbacks.isEmpty());
+    }
+
+    @Test
+    public void submitRequest() {
+        mService.setBaseState(DEFAULT_DEVICE_STATE);
+
+        TestDeviceStateListener listener = new TestDeviceStateListener();
+        mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+                ConcurrentUtils.DIRECT_EXECUTOR);
+
+        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+        DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+        mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+        assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+        mDeviceStateManagerGlobal.cancelRequest(request);
+
+        assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
     }
 
     private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
@@ -100,8 +137,23 @@
         }
     }
 
-    private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
-        private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+    private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
+        public static final class Request {
+            public final IBinder token;
+            public final int state;
+            public final int flags;
+
+            private Request(IBinder token, int state, int flags) {
+                this.token = token;
+                this.state = state;
+                this.flags = flags;
+            }
+        }
+
+        private int mBaseState = DEFAULT_DEVICE_STATE;
+        private int mMergedState = DEFAULT_DEVICE_STATE;
+        private ArrayList<Request> mRequests = new ArrayList<>();
+
         private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
 
         @Override
@@ -112,19 +164,86 @@
 
             mCallbacks.add(callback);
             try {
-                callback.onDeviceStateChanged(mDeviceState);
+                callback.onDeviceStateChanged(mMergedState);
             } catch (RemoteException e) {
                 // Do nothing. Should never happen.
             }
         }
 
-        public void setDeviceState(int deviceState) {
-            boolean stateChanged = mDeviceState != deviceState;
-            mDeviceState = deviceState;
-            if (stateChanged) {
+        @Override
+        public int[] getSupportedDeviceStates() {
+            return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
+        }
+
+        @Override
+        public void requestState(IBinder token, int state, int flags) {
+            if (!mRequests.isEmpty()) {
+                final Request topRequest = mRequests.get(mRequests.size() - 1);
                 for (IDeviceStateManagerCallback callback : mCallbacks) {
                     try {
-                        callback.onDeviceStateChanged(mDeviceState);
+                        callback.onRequestSuspended(topRequest.token);
+                    } catch (RemoteException e) {
+                        // Do nothing. Should never happen.
+                    }
+                }
+            }
+
+            final Request request = new Request(token, state, flags);
+            mRequests.add(request);
+            notifyStateChangedIfNeeded();
+
+            for (IDeviceStateManagerCallback callback : mCallbacks) {
+                try {
+                    callback.onRequestActive(token);
+                } catch (RemoteException e) {
+                    // Do nothing. Should never happen.
+                }
+            }
+        }
+
+        @Override
+        public void cancelRequest(IBinder token) {
+            int index = -1;
+            for (int i = 0; i < mRequests.size(); i++) {
+                if (mRequests.get(i).token.equals(token)) {
+                    index = i;
+                    break;
+                }
+            }
+
+            if (index == -1) {
+                throw new IllegalArgumentException("Unknown request: " + token);
+            }
+
+            mRequests.remove(index);
+            for (IDeviceStateManagerCallback callback : mCallbacks) {
+                try {
+                    callback.onRequestCanceled(token);
+                } catch (RemoteException e) {
+                    // Do nothing. Should never happen.
+                }
+            }
+            notifyStateChangedIfNeeded();
+        }
+
+        public void setBaseState(int state) {
+            mBaseState = state;
+            notifyStateChangedIfNeeded();
+        }
+
+        private void notifyStateChangedIfNeeded() {
+            final int originalMergedState = mMergedState;
+
+            if (!mRequests.isEmpty()) {
+                mMergedState = mRequests.get(mRequests.size() - 1).state;
+            } else {
+                mMergedState = mBaseState;
+            }
+
+            if (mMergedState != originalMergedState) {
+                for (IDeviceStateManagerCallback callback : mCallbacks) {
+                    try {
+                        callback.onDeviceStateChanged(mMergedState);
                     } catch (RemoteException e) {
                         // Do nothing. Should never happen.
                     }
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-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.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
-    private static final int APP_WIDTH = 272;
-    private static final int APP_HEIGHT = 700;
-    // Tablet size device, ROTATION_0 corresponds to portrait.
-    private static final int LOGICAL_WIDTH = 700;
-    private static final int LOGICAL_HEIGHT = 1800;
-
-    // Bounds of the app when the device is in portrait mode.
-    private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
-    private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
-    private StaticMockitoSession mMockitoSession;
-
-    private DisplayManagerGlobal mDisplayManagerGlobal;
-    private Context mApplicationContext;
-    private DisplayInfo mDisplayInfo = new DisplayInfo();
-
-    @Before
-    public void setupTests() {
-        mMockitoSession = mockitoSession()
-                .mockStatic(DisplayManagerGlobal.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-
-        // Ensure no adjustments are set before each test.
-        mApplicationContext = ApplicationProvider.getApplicationContext();
-        DisplayAdjustments displayAdjustments =
-                mApplicationContext.getResources().getDisplayAdjustments();
-        displayAdjustments.setFixedRotationAdjustments(null);
-        mApplicationContext.getResources().overrideDisplayAdjustments(null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
-                null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
-                null);
-        mDisplayInfo.rotation = ROTATION_0;
-
-        mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
-        doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
-    }
-
-    @After
-    public void teardownTests() {
-        if (mMockitoSession != null) {
-            mMockitoSession.finishMocking();
-        }
-        Mockito.framework().clearInlineMocks();
-    }
-
-    @Test
-    public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testConstructor_defaultResources_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                mApplicationContext.getResources().getDisplayAdjustments());
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-        // GIVEN display is constructed with display adjustments.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                displayAdjustments);
-        // THEN rotation is not adjusted since no override was set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is not adjusted since no override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is adjusted since an override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_90);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated with an override.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    // Given rotated display dimensions, calculate the letterboxed app bounds.
-    private static Rect buildAppBounds(int displayWidth, int displayHeight) {
-        final int midWidth = displayWidth / 2;
-        final int left = midWidth - (APP_WIDTH / 2);
-        final int right = midWidth + (APP_WIDTH / 2);
-        final int midHeight = displayHeight / 2;
-        // Coordinate system starts at top left.
-        final int top = midHeight - (APP_HEIGHT / 2);
-        final int bottom = midHeight + (APP_HEIGHT / 2);
-        return new Rect(left, top, right, bottom);
-    }
-
-    private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_90;
-        // Flip width & height assignment since the device is rotated.
-        displayInfo.logicalWidth = LOGICAL_HEIGHT;
-        displayInfo.logicalHeight = LOGICAL_WIDTH;
-    }
-
-    private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_0;
-        displayInfo.logicalWidth = LOGICAL_WIDTH;
-        displayInfo.logicalHeight = LOGICAL_HEIGHT;
-    }
-
-    /**
-     * Set max bounds to be sandboxed to the app bounds, indicating the app is in
-     * size compat mode or letterbox.
-     */
-    private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
-        resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
-    }
-
-    /**
-     * Do not compare entire display info, since it is updated to match display the test is run on.
-     */
-    private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
-        assertThat(actual.displayId).isEqualTo(expected.displayId);
-        assertThat(actual.rotation).isEqualTo(expected.rotation);
-        assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
-        assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeIsLandscape(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        // Flip the width and height check since the device is rotated.
-        assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
-    }
-
-    private static void verifyRealMetricsIsLandscape(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        // Flip the width and height check since the device is rotated.
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
-    }
-
-    private static void verifyRealSizeIsPortrait(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
-    }
-
-    private static void verifyRealMetricsIsPortrait(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
-    }
-
-    private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
-        assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
-    }
-
-    private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
-            Resources resources, @Surface.Rotation int rotation) {
-        FixedRotationAdjustments fixedRotationAdjustments =
-                setFixedRotationAdjustments(resources, rotation);
-        resources.overrideDisplayAdjustments(
-                buildOverrideRotationAdjustments(fixedRotationAdjustments));
-        return fixedRotationAdjustments;
-    }
-
-    private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
-            @Surface.Rotation int rotation) {
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
-        return fixedRotationAdjustments;
-    }
-
-    private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
-            FixedRotationAdjustments fixedRotationAdjustments) {
-        return consumedDisplayAdjustments
-                -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-    }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index b0ae9b9..ea42246 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -165,6 +165,7 @@
     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
+    <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
 
     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index cdc61a1..0484a9a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -465,10 +465,15 @@
         <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
         <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
+        <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
+        <permission name="android.permission.MANAGE_MUSIC_RECOGNITION" />
         <!-- Permission required for CTS test - CallLogTest -->
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
         <permission name="android.permission.MODIFY_QUIET_MODE" />
+        <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+        <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
+        <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -482,6 +487,8 @@
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
         <!-- Permissions required for quick settings tile -->
         <permission name="android.permission.STATUS_BAR"/>
+        <!-- Permissions required to query Betterbug -->
+        <permission name="android.permission.QUERY_ALL_PACKAGES"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "123161180": {
+      "message": "SEVER CHILDREN",
+      "level": "INFO",
+      "group": "WM_SHOW_TRANSACTIONS",
+      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+    },
     "140319294": {
       "message": "IME target changed within ActivityRecord",
       "level": "DEBUG",
@@ -2137,12 +2143,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "332390227": {
-      "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "342460966": {
       "message": "DRAG %s: pos=(%d,%d)",
       "level": "INFO",
@@ -2623,12 +2623,6 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
     },
-    "910200295": {
-      "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "913494177": {
       "message": "removeAllWindowsIfPossible: removing win=%s",
       "level": "WARN",
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index bb795cd..63df0db 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,8 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.text.FontConfig;
+import android.text.TextUtils;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -45,6 +47,27 @@
  */
 public class FontListParser {
 
+    // XML constants for FontFamily.
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_LANG = "lang";
+    private static final String ATTR_VARIANT = "variant";
+    private static final String TAG_FONT = "font";
+    private static final String VARIANT_COMPACT = "compact";
+    private static final String VARIANT_ELEGANT = "elegant";
+
+    // XML constants for Font.
+    public static final String ATTR_INDEX = "index";
+    public static final String ATTR_WEIGHT = "weight";
+    public static final String ATTR_STYLE = "style";
+    public static final String ATTR_FALLBACK_FOR = "fallbackFor";
+    public static final String STYLE_ITALIC = "italic";
+    public static final String STYLE_NORMAL = "normal";
+    public static final String TAG_AXIS = "axis";
+
+    // XML constants for FontVariationAxis.
+    public static final String ATTR_TAG = "tag";
+    public static final String ATTR_STYLEVALUE = "stylevalue";
+
     /* Parse fallback list (no names) */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
@@ -148,7 +171,7 @@
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             final String tag = parser.getName();
-            if (tag.equals("font")) {
+            if (tag.equals(TAG_FONT)) {
                 fonts.add(readFont(parser, fontDir, updatableFontMap));
             } else {
                 skip(parser);
@@ -156,15 +179,41 @@
         }
         int intVariant = FontConfig.FontFamily.VARIANT_DEFAULT;
         if (variant != null) {
-            if (variant.equals("compact")) {
+            if (variant.equals(VARIANT_COMPACT)) {
                 intVariant = FontConfig.FontFamily.VARIANT_COMPACT;
-            } else if (variant.equals("elegant")) {
+            } else if (variant.equals(VARIANT_ELEGANT)) {
                 intVariant = FontConfig.FontFamily.VARIANT_ELEGANT;
             }
         }
         return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
     }
 
+    /**
+     * Write a family tag representing {@code fontFamily}. The tag should be started by the caller.
+     */
+    public static void writeFamily(TypedXmlSerializer out, FontConfig.FontFamily fontFamily)
+            throws IOException {
+        if (!TextUtils.isEmpty(fontFamily.getName())) {
+            out.attribute(null, ATTR_NAME, fontFamily.getName());
+        }
+        if (!fontFamily.getLocaleList().isEmpty()) {
+            out.attribute(null, ATTR_LANG, fontFamily.getLocaleList().toLanguageTags());
+        }
+        switch (fontFamily.getVariant()) {
+            case FontConfig.FontFamily.VARIANT_COMPACT:
+                out.attribute(null, ATTR_VARIANT, VARIANT_COMPACT);
+                break;
+            case FontConfig.FontFamily.VARIANT_ELEGANT:
+                out.attribute(null, ATTR_VARIANT, VARIANT_ELEGANT);
+                break;
+        }
+        for (FontConfig.Font font : fontFamily.getFontList()) {
+            out.startTag(null, TAG_FONT);
+            writeFont(out, font);
+            out.endTag(null, TAG_FONT);
+        }
+    }
+
     /** Matches leading and trailing XML whitespace. */
     private static final Pattern FILENAME_WHITESPACE_PATTERN =
             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
@@ -175,13 +224,13 @@
             @Nullable Map<String, File> updatableFontMap)
             throws XmlPullParserException, IOException {
 
-        String indexStr = parser.getAttributeValue(null, "index");
+        String indexStr = parser.getAttributeValue(null, ATTR_INDEX);
         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
         List<FontVariationAxis> axes = new ArrayList<>();
-        String weightStr = parser.getAttributeValue(null, "weight");
-        int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
-        boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
-        String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
+        String weightStr = parser.getAttributeValue(null, ATTR_WEIGHT);
+        int weight = weightStr == null ? FontStyle.FONT_WEIGHT_NORMAL : Integer.parseInt(weightStr);
+        boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
+        String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
         StringBuilder filename = new StringBuilder();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -189,7 +238,7 @@
             }
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
-            if (tag.equals("axis")) {
+            if (tag.equals(TAG_AXIS)) {
                 axes.add(readAxis(parser));
             } else {
                 skip(parser);
@@ -237,14 +286,48 @@
         return null;
     }
 
+    private static void writeFont(TypedXmlSerializer out, FontConfig.Font font)
+            throws IOException {
+        if (font.getTtcIndex() != 0) {
+            out.attributeInt(null, ATTR_INDEX, font.getTtcIndex());
+        }
+        if (font.getStyle().getWeight() != FontStyle.FONT_WEIGHT_NORMAL) {
+            out.attributeInt(null, ATTR_WEIGHT, font.getStyle().getWeight());
+        }
+        if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
+            out.attribute(null, ATTR_STYLE, STYLE_ITALIC);
+        } else {
+            out.attribute(null, ATTR_STYLE, STYLE_NORMAL);
+        }
+        if (!TextUtils.isEmpty(font.getFontFamilyName())) {
+            out.attribute(null, ATTR_FALLBACK_FOR, font.getFontFamilyName());
+        }
+        out.text(font.getFile().getName());
+        FontVariationAxis[] axes =
+                FontVariationAxis.fromFontVariationSettings(font.getFontVariationSettings());
+        if (axes != null) {
+            for (FontVariationAxis axis : axes) {
+                out.startTag(null, TAG_AXIS);
+                writeAxis(out, axis);
+                out.endTag(null, TAG_AXIS);
+            }
+        }
+    }
+
     private static FontVariationAxis readAxis(XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        String tagStr = parser.getAttributeValue(null, "tag");
-        String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+        String tagStr = parser.getAttributeValue(null, ATTR_TAG);
+        String styleValueStr = parser.getAttributeValue(null, ATTR_STYLEVALUE);
         skip(parser);  // axis tag is empty, ignore any contents and consume end tag
         return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
     }
 
+    private static void writeAxis(TypedXmlSerializer out, FontVariationAxis axis)
+            throws IOException {
+        out.attribute(null, ATTR_TAG, axis.getTag());
+        out.attributeFloat(null, ATTR_STYLEVALUE, axis.getStyleValue());
+    }
+
     /**
      * Reads alias elements
      */
@@ -255,7 +338,7 @@
         String weightStr = parser.getAttributeValue(null, "weight");
         int weight;
         if (weightStr == null) {
-            weight = 400;
+            weight = FontStyle.FONT_WEIGHT_NORMAL;
         } else {
             weight = Integer.parseInt(weightStr);
         }
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index cb4dd9e..b70fa0e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -46,7 +46,6 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Optional;
 import java.util.concurrent.Executor;
 import java.util.stream.Stream;
@@ -1148,24 +1147,14 @@
                             // Default to SRGB if the display doesn't support wide color
                             .orElse(Dataspace.SRGB);
 
-            float maxRefreshRate =
-                    (float) Arrays.stream(display.getSupportedModes())
-                            .mapToDouble(Mode::getRefreshRate)
-                            .max()
-                            .orElseGet(() -> {
-                                Log.i(LOG_TAG, "Failed to find the maximum display refresh rate");
-                                // Assume that the max refresh rate is 60hz if we can't find one.
-                                return 60.0;
-                            });
             // Grab the physical screen dimensions from the active display mode
             // Strictly speaking the screen resolution may not always be constant - it is for
             // sizing the font cache for the underlying rendering thread. Since it's a
             // heuristic we don't need to be always 100% correct.
             Mode activeMode = display.getMode();
             nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
-                    display.getRefreshRate(), maxRefreshRate,
-                    wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(),
-                    display.getPresentationDeadlineNanos());
+                    display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
+                    display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
 
             // Defensively clear out the context
             mContext = null;
@@ -1324,6 +1313,5 @@
     private static native void nSetDisplayDensityDpi(int densityDpi);
 
     private static native void nInitDisplayInfo(int width, int height, float refreshRate,
-            float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos,
-            long presentationDeadlineNanos);
+            int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
 }
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 496e470..ad4c3fe 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -190,7 +190,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the provided RenderEffect
+     * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
      *
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      * @param renderEffect Source to be transformed by the specified {@link ColorFilter}
@@ -209,7 +209,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the contents of the
+     * Create a {@link RenderEffect} that applies the color filter to the contents of the
      * {@link android.graphics.RenderNode} that this RenderEffect is installed on
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      */
@@ -224,7 +224,7 @@
     }
 
     /**
-     * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
+     * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
      * combined by the specified {@link BlendMode}
      *
      * @param dst The Dst pixels used in blending
@@ -248,8 +248,8 @@
     }
 
     /**
-     * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
-     * treated as the source bitmap passed to 'outer', i.e.
+     * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
+     * 'inner' are treated as the source bitmap passed to 'outer', i.e.
      *
      * <pre>
      * {@code
@@ -278,6 +278,18 @@
             );
     }
 
+    /**
+     * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
+     * This is useful to create an input for other {@link RenderEffect} types such as
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
+     * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
+     */
+    @NonNull
+    public static RenderEffect createShaderEffect(@NonNull Shader shader) {
+        return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
+    }
+
     private final long mNativeRenderEffect;
 
     /* only constructed from static factory methods */
@@ -305,5 +317,6 @@
     private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
     private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
     private static native long nativeCreateChainEffect(long outer, long inner);
+    private static native long nativeCreateShaderEffect(long shader);
     private static native long nativeGetFinalizer();
 }
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9214ff1..b153c99 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,8 +26,7 @@
 import android.graphics.RectF;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.LongSparseArray;
+import android.text.TextUtils;
 import android.util.LongSparseLongArray;
 import android.util.TypedValue;
 
@@ -44,7 +43,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.channels.FileChannel;
@@ -61,14 +59,9 @@
     private static final int STYLE_ITALIC = 1;
     private static final int STYLE_NORMAL = 0;
 
-    private static final Object MAP_LOCK = new Object();
-    // We need to have mapping from native ptr to Font object for later accessing from TextShape
-    // result since Typeface doesn't have reference to Font object and it is not always created from
-    // Font object. Sometimes Typeface is created in native layer only and there might not be Font
-    // object in Java layer. So, if not found in this cache, create new Font object for API user.
-    @GuardedBy("MAP_LOCK")
-    private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
-            new LongSparseArray<>();
+    private static final NativeAllocationRegistry BUFFER_REGISTRY =
+            NativeAllocationRegistry.createMalloced(
+                    ByteBuffer.class.getClassLoader(), nGetReleaseNativeFont());
 
     private static final Object SOURCE_ID_LOCK = new Object();
     @GuardedBy("SOURCE_ID_LOCK")
@@ -79,9 +72,7 @@
      * A builder class for creating new Font.
      */
     public static final class Builder {
-        private static final NativeAllocationRegistry sFontRegistry =
-                NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
-                    nGetReleaseNativeFont());
+
 
         private @Nullable ByteBuffer mBuffer;
         private @Nullable File mFile;
@@ -484,26 +475,15 @@
             final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
 
             long ptr;
-            int fontIdentifier;
+            final Font font;
             if (mFont == null) {
                 ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
                         mTtcIndex);
-                long fontBufferPtr = nGetFontBufferAddress(ptr);
-                synchronized (SOURCE_ID_LOCK) {
-                    long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
-                    if (id == -1) {
-                        id = FONT_SOURCE_ID_MAP.size();
-                        FONT_SOURCE_ID_MAP.put(fontBufferPtr, id);
-                    }
-                    fontIdentifier = (int) id;
-                }
+                font = new Font(ptr);
             } else {
                 ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
-                fontIdentifier = mFont.mSourceIdentifier;
+                font = new Font(ptr);
             }
-            final Font font = new Font(ptr, readonlyBuffer, mFile,
-                    new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier);
-            sFontRegistry.registerNativeAllocation(font, ptr);
             return font;
         }
 
@@ -525,33 +505,32 @@
     }
 
     private final long mNativePtr;  // address of the shared ptr of minikin::Font
-    private final @NonNull ByteBuffer mBuffer;
-    private final @Nullable File mFile;
-    private final FontStyle mFontStyle;
-    private final @IntRange(from = 0) int mTtcIndex;
-    private final @Nullable FontVariationAxis[] mAxes;
-    private final @NonNull String mLocaleList;
-    private final int mSourceIdentifier;  // An identifier of font source data.
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private @NonNull ByteBuffer mBuffer = null;
+    @GuardedBy("mLock")
+    private boolean mIsFileInitialized = false;
+    @GuardedBy("mLock")
+    private @Nullable File mFile = null;
+    @GuardedBy("mLock")
+    private FontStyle mFontStyle = null;
+    @GuardedBy("mLock")
+    private @Nullable FontVariationAxis[] mAxes = null;
+    @GuardedBy("mLock")
+    private @NonNull LocaleList mLocaleList = null;
+    @GuardedBy("mLock")
+    private int mSourceIdentifier = -1;
 
     /**
      * Use Builder instead
+     *
+     * Caller must increment underlying minikin::Font ref count.
+     *
+     * @hide
      */
-    private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
-            @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
-            @Nullable FontVariationAxis[] axes, @NonNull String localeList,
-            int sourceIdentifier) {
-        mBuffer = buffer;
-        mFile = file;
-        mFontStyle = fontStyle;
+    public Font(long nativePtr) {
         mNativePtr = nativePtr;
-        mTtcIndex = ttcIndex;
-        mAxes = axes;
-        mLocaleList = localeList;
-        mSourceIdentifier = sourceIdentifier;
-
-        synchronized (MAP_LOCK) {
-            FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this));
-        }
     }
 
     /**
@@ -563,7 +542,22 @@
      * @return a font buffer
      */
     public @NonNull ByteBuffer getBuffer() {
-        return mBuffer;
+        synchronized (mLock) {
+            if (mBuffer == null) {
+                // Create new instance of native FontWrapper, i.e. incrementing ref count of
+                // minikin Font instance for keeping buffer fo ByteBuffer reference which may live
+                // longer than this object.
+                long ref = nCloneFont(mNativePtr);
+                ByteBuffer fromNative = nNewByteBuffer(mNativePtr);
+
+                // Bind ByteBuffer's lifecycle with underlying font object.
+                BUFFER_REGISTRY.registerNativeAllocation(fromNative, ref);
+
+                // JNI NewDirectBuffer creates writable ByteBuffer even if it is mmaped readonly.
+                mBuffer = fromNative.asReadOnlyBuffer();
+            }
+            return mBuffer;
+        }
     }
 
     /**
@@ -574,7 +568,16 @@
      * @return a file path of the font
      */
     public @Nullable File getFile() {
-        return mFile;
+        synchronized (mLock) {
+            if (!mIsFileInitialized) {
+                String path = nGetFontPath(mNativePtr);
+                if (!TextUtils.isEmpty(path)) {
+                    mFile = new File(path);
+                }
+                mIsFileInitialized = true;
+            }
+            return mFile;
+        }
     }
 
     /**
@@ -585,7 +588,16 @@
      * @return a font style
      */
     public @NonNull FontStyle getStyle() {
-        return mFontStyle;
+        synchronized (mLock) {
+            if (mFontStyle == null) {
+                int packedStyle = nGetPackedStyle(mNativePtr);
+                mFontStyle = new FontStyle(
+                        FontFileUtil.unpackWeight(packedStyle),
+                        FontFileUtil.unpackItalic(packedStyle)
+                                ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+            }
+            return mFontStyle;
+        }
     }
 
     /**
@@ -597,7 +609,7 @@
      * @return a TTC index value
      */
     public @IntRange(from = 0) int getTtcIndex() {
-        return mTtcIndex;
+        return nGetIndex(mNativePtr);
     }
 
     /**
@@ -608,7 +620,23 @@
      * @return font variation settings
      */
     public @Nullable FontVariationAxis[] getAxes() {
-        return mAxes == null ? null : mAxes.clone();
+        synchronized (mLock) {
+            if (mAxes == null) {
+                int axisCount = nGetAxisCount(mNativePtr);
+                mAxes = new FontVariationAxis[axisCount];
+                char[] charBuffer = new char[4];
+                for (int i = 0; i < axisCount; ++i) {
+                    long packedAxis = nGetAxisInfo(mNativePtr, i);
+                    float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+                    charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >>> 56);
+                    charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >>> 48);
+                    charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >>> 40);
+                    charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >>> 32);
+                    mAxes[i] = new FontVariationAxis(new String(charBuffer), value);
+                }
+            }
+        }
+        return mAxes;
     }
 
     /**
@@ -618,7 +646,17 @@
      * @return a locale list
      */
     public @NonNull LocaleList getLocaleList() {
-        return LocaleList.forLanguageTags(mLocaleList);
+        synchronized (mLock) {
+            if (mLocaleList == null) {
+                String langTags = nGetLocaleList(mNativePtr);
+                if (TextUtils.isEmpty(langTags)) {
+                    mLocaleList = LocaleList.getEmptyLocaleList();
+                } else {
+                    mLocaleList = LocaleList.forLanguageTags(langTags);
+                }
+            }
+            return mLocaleList;
+        }
     }
 
     /**
@@ -713,7 +751,20 @@
      * @return an unique identifier for the font source data.
      */
     public int getSourceIdentifier() {
-        return mSourceIdentifier;
+        synchronized (mLock) {
+            if (mSourceIdentifier == -1) {
+                long bufferAddress = nGetBufferAddress(mNativePtr);
+                synchronized (SOURCE_ID_LOCK) {
+                    long id = FONT_SOURCE_ID_MAP.get(bufferAddress, -1);
+                    if (id == -1) {
+                        id = FONT_SOURCE_ID_MAP.size();
+                        FONT_SOURCE_ID_MAP.append(bufferAddress, id);
+                    }
+                    mSourceIdentifier = (int) id;
+                }
+            }
+            return mSourceIdentifier;
+        }
     }
 
     /**
@@ -736,13 +787,16 @@
     private boolean isSameSource(@NonNull Font other) {
         Objects.requireNonNull(other);
 
+        ByteBuffer myBuffer = getBuffer();
+        ByteBuffer otherBuffer = other.getBuffer();
+
         // Shortcut for the same instance.
-        if (mBuffer == other.mBuffer) {
+        if (myBuffer == otherBuffer) {
             return true;
         }
 
         // Shortcut for different font buffer check by comparing size.
-        if (mBuffer.capacity() != other.mBuffer.capacity()) {
+        if (myBuffer.capacity() != otherBuffer.capacity()) {
             return false;
         }
 
@@ -750,15 +804,15 @@
         // underlying native font object holds buffer address, check if this buffer points exactly
         // the same address as a shortcut of equality. For being compatible with of API30 or before,
         // check buffer position even if the buffer points the same address.
-        if (mSourceIdentifier == other.mSourceIdentifier
-                && mBuffer.position() == other.mBuffer.position()) {
+        if (getSourceIdentifier() == other.getSourceIdentifier()
+                && myBuffer.position() == otherBuffer.position()) {
             return true;
         }
 
         // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
         // file but has the same file size, or two font has same content but they are allocated
         // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
-        return mBuffer.equals(other.mBuffer);
+        return myBuffer.equals(otherBuffer);
     }
 
     @Override
@@ -769,10 +823,20 @@
         if (!(o instanceof Font)) {
             return false;
         }
+
         Font f = (Font) o;
-        boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
-                && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
-                && Objects.equals(mFile, f.mFile);
+
+        // The underlying minikin::Font object is the source of the truth of font information. Thus,
+        // Pointer equality is the object equality.
+        if (nGetMinikinFontPtr(mNativePtr) == nGetMinikinFontPtr(f.mNativePtr)) {
+            return true;
+        }
+
+        boolean paramEqual = f.getStyle().equals(getStyle())
+                && f.getTtcIndex() == getTtcIndex()
+                && Arrays.equals(f.getAxes(), getAxes())
+                && Objects.equals(f.getLocaleList(), getLocaleList())
+                && Objects.equals(getFile(), f.getFile());
 
         if (!paramEqual) {
             return false;
@@ -784,64 +848,42 @@
     @Override
     public int hashCode() {
         return Objects.hash(
-                mFontStyle,
-                mTtcIndex,
-                Arrays.hashCode(mAxes),
+                getStyle(),
+                getTtcIndex(),
+                Arrays.hashCode(getAxes()),
                 // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
                 // data which is not performant e.g. for HashMap. The hash collision are less likely
                 // happens because it is unlikely happens the different font files has exactly the
                 // same size.
-                mLocaleList);
+                getLocaleList());
     }
 
     @Override
     public String toString() {
         return "Font {"
-            + "path=" + mFile
-            + ", style=" + mFontStyle
-            + ", ttcIndex=" + mTtcIndex
-            + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
-            + ", localeList=" + mLocaleList
-            + ", buffer=" + mBuffer
+            + "path=" + getFile()
+            + ", style=" + getStyle()
+            + ", ttcIndex=" + getTtcIndex()
+            + ", axes=" + FontVariationAxis.toFontVariationSettings(getAxes())
+            + ", localeList=" + getLocaleList()
+            + ", buffer=" + getBuffer()
             + "}";
     }
 
-    /**
-     * Lookup Font object from native pointer or create new one if not found.
-     * @hide
-     */
-    public static Font findOrCreateFontFromNativePtr(long ptr) {
-        // First, lookup from known mapps.
-        synchronized (MAP_LOCK) {
-            WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
-            if (fontRef != null) {
-                Font font = fontRef.get();
-                if (font != null) {
-                    return font;
-                }
-            }
+    @CriticalNative
+    private static native long nGetMinikinFontPtr(long font);
 
-            // If not found, create Font object from native object for Java API users.
-            ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
-            NativeFont.Font font = NativeFont.readNativeFont(ptr);
+    @CriticalNative
+    private static native long nCloneFont(long font);
 
-            Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
-                    .setWeight(font.getStyle().getWeight())
-                    .setSlant(font.getStyle().getSlant())
-                    .setTtcIndex(font.getIndex())
-                    .setFontVariationSettings(font.getAxes());
+    @FastNative
+    private static native ByteBuffer nNewByteBuffer(long font);
 
-            Font newFont = null;
-            try {
-                newFont = builder.build();
-                FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
-            } catch (IOException e) {
-                // This must not happen since the buffer was already created once.
-                Log.e("Font", "Failed to create font object from existing buffer.", e);
-            }
-            return newFont;
-        }
-    }
+    @CriticalNative
+    private static native long nGetBufferAddress(long font);
+
+    @CriticalNative
+    private static native long nGetReleaseNativeFont();
 
     @FastNative
     private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
@@ -849,9 +891,21 @@
     @FastNative
     private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
 
-    @CriticalNative
-    private static native long nGetNativeFontPtr(long ptr);
+    @FastNative
+    private static native String nGetFontPath(long fontPtr);
+
+    @FastNative
+    private static native String nGetLocaleList(long familyPtr);
 
     @CriticalNative
-    private static native long nGetFontBufferAddress(long font);
+    private static native int nGetPackedStyle(long fontPtr);
+
+    @CriticalNative
+    private static native int nGetIndex(long fontPtr);
+
+    @CriticalNative
+    private static native int nGetAxisCount(long fontPtr);
+
+    @CriticalNative
+    private static native long nGetAxisInfo(long fontPtr, int i);
 }
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 77f86fe..8c13d3e 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -143,12 +143,10 @@
         private static native long nGetReleaseNativeFamily();
     }
 
-    private final ArrayList<Font> mFonts;
     private final long mNativePtr;
 
     // Use Builder instead.
     private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
-        mFonts = fonts;
         mNativePtr = ptr;
     }
 
@@ -176,7 +174,10 @@
      * @return a registered font
      */
     public @NonNull Font getFont(@IntRange(from = 0) int index) {
-        return mFonts.get(index);
+        if (index < 0 || getSize() <= index) {
+            throw new IndexOutOfBoundsException();
+        }
+        return new Font(nGetFont(mNativePtr, index));
     }
 
     /**
@@ -185,7 +186,7 @@
      * @return the number of fonts registered in this family.
      */
     public @IntRange(from = 1) int getSize() {
-        return mFonts.size();
+        return nGetFontSize(mNativePtr);
     }
 
     /** @hide */
@@ -193,6 +194,12 @@
         return mNativePtr;
     }
 
+    @CriticalNative
+    private static native int nGetFontSize(long family);
+
+    @CriticalNative
+    private static native long nGetFont(long family, int i);
+
     @FastNative
     private static native String nGetLangTags(long family);
 
diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java
deleted file mode 100644
index 9e9d76a..0000000
--- a/graphics/java/android/graphics/fonts/NativeFont.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.fonts;
-
-import android.graphics.Typeface;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Read native font objects.
- *
- * @hide
- */
-public class NativeFont {
-
-    /**
-     * Represents native font object.
-     */
-    public static final class Font {
-        private final File mFile;
-        private final int mIndex;
-        private final FontVariationAxis[] mAxes;
-        private final FontStyle mStyle;
-
-        public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
-            mFile = file;
-            mIndex = index;
-            mAxes = axes;
-            mStyle = style;
-        }
-
-        public File getFile() {
-            return mFile;
-        }
-
-        public FontVariationAxis[] getAxes() {
-            return mAxes;
-        }
-
-        public FontStyle getStyle() {
-            return mStyle;
-        }
-
-        public int getIndex() {
-            return mIndex;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Font font = (Font) o;
-            return mIndex == font.mIndex && mFile.equals(font.mFile)
-                    && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = Objects.hash(mFile, mIndex, mStyle);
-            result = 31 * result + Arrays.hashCode(mAxes);
-            return result;
-        }
-    }
-
-    /**
-     * Represents native font family object.
-     */
-    public static final class Family {
-        private final List<Font> mFonts;
-        private final String mLocale;
-
-        public Family(List<Font> fonts, String locale) {
-            mFonts = fonts;
-            mLocale = locale;
-        }
-
-        public List<Font> getFonts() {
-            return mFonts;
-        }
-
-        public String getLocale() {
-            return mLocale;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Family family = (Family) o;
-            return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mFonts, mLocale);
-        }
-    }
-
-    /**
-     * Get underlying font families from Typeface
-     *
-     * @param typeface a typeface
-     * @return list of family
-     */
-    public static List<Family> readTypeface(Typeface typeface) {
-        int familyCount = nGetFamilyCount(typeface.native_instance);
-        List<Family> result = new ArrayList<>(familyCount);
-        for (int i = 0; i < familyCount; ++i) {
-            result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
-        }
-        return result;
-    }
-
-    /**
-     * Read family object from native pointer
-     *
-     * @param familyPtr a font family pointer
-     * @return a family
-     */
-    public static Family readNativeFamily(long familyPtr) {
-        int fontCount = nGetFontCount(familyPtr);
-        List<Font> result = new ArrayList<>(fontCount);
-        for (int i = 0; i < fontCount; ++i) {
-            result.add(readNativeFont(nGetFont(familyPtr, i)));
-        }
-        String localeList = nGetLocaleList(familyPtr);
-        return new Family(result, localeList);
-    }
-
-    /**
-     * Read font object from native pointer.
-     *
-     * @param ptr a font pointer
-     * @return a font
-     */
-    public static Font readNativeFont(long ptr) {
-        long packed = nGetFontInfo(ptr);
-        int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
-        boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
-        int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
-        int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
-        FontVariationAxis[] axes = new FontVariationAxis[axisCount];
-        char[] charBuffer = new char[4];
-        for (int i = 0; i < axisCount; ++i) {
-            long packedAxis = nGetAxisInfo(ptr, i);
-            float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
-            charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
-            charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
-            charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
-            charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
-            axes[i] = new FontVariationAxis(new String(charBuffer), value);
-        }
-        String path = nGetFontPath(ptr);
-        File file = (path == null) ? null : new File(path);
-        FontStyle style = new FontStyle(weight,
-                italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
-
-        return new Font(file, ttcIndex, axes, style);
-    }
-
-    @CriticalNative
-    private static native int nGetFamilyCount(long ptr);
-
-    @CriticalNative
-    private static native long nGetFamily(long ptr, int index);
-
-    @FastNative
-    private static native String nGetLocaleList(long familyPtr);
-
-    @CriticalNative
-    private static native long nGetFont(long familyPtr, int fontIndex);
-
-    @CriticalNative
-    private static native int nGetFontCount(long familyPtr);
-
-    @CriticalNative
-    private static native long nGetFontInfo(long fontPtr);
-
-    @CriticalNative
-    private static native long nGetAxisInfo(long fontPtr, int i);
-
-    @FastNative
-    private static native String nGetFontPath(long fontPtr);
-}
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
deleted file mode 100644
index 5655e7f..0000000
--- a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.fonts;
-
-import android.annotation.NonNull;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.nio.ByteBuffer;
-
-/**
- * This is a helper class for showing native allocated buffer in Java API.
- *
- * @hide
- */
-public class NativeFontBufferHelper {
-    private NativeFontBufferHelper() {}
-
-    private static final NativeAllocationRegistry REGISTRY =
-            NativeAllocationRegistry.createMalloced(
-                    ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
-
-    /**
-     * Wrap native buffer with ByteBuffer with adding reference to it.
-     */
-    public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
-        long refPtr = nRefFontBuffer(fontPtr);
-        ByteBuffer buffer = nWrapByteBuffer(refPtr);
-
-        // Releasing native object so that decreasing shared pointer ref count when the byte buffer
-        // is GCed.
-        REGISTRY.registerNativeAllocation(buffer, refPtr);
-
-        return buffer;
-    }
-
-    @CriticalNative
-    private static native long nRefFontBuffer(long fontPtr);
-
-    @FastNative
-    private static native ByteBuffer nWrapByteBuffer(long refPtr);
-
-    @CriticalNative
-    private static native long nGetReleaseFunc();
-}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index c2de0ac..8d20e9c 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -184,7 +184,7 @@
             long ptr = nGetFont(layoutPtr, i);
             if (prevPtr != ptr) {
                 prevPtr = ptr;
-                prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+                prevFont = new Font(ptr);
             }
             mFonts.add(prevFont);
         }
@@ -224,9 +224,7 @@
             if (getGlyphId(i) != that.getGlyphId(i)) return false;
             if (getGlyphX(i) != that.getGlyphX(i)) return false;
             if (getGlyphY(i) != that.getGlyphY(i)) return false;
-            // Intentionally using reference equality since font equality is heavy due to buffer
-            // compare.
-            if (getFont(i) != that.getFont(i)) return false;
+            if (!getFont(i).equals(that.getFont(i))) return false;
         }
 
         return true;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9..d188b65 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@
         keyDescriptor.blob = wrappedKey;
         keyDescriptor.domain = wrappedKeyDescriptor.domain;
 
-        return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+        return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
                 wrappingKeyDescriptor, maskingKey,
                 args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
     }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 16bf546..0871517 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -434,14 +434,16 @@
     @NonNull
     public static java.security.KeyStore getKeyStoreForUid(int uid)
             throws KeyStoreException, NoSuchProviderException {
-        String providerName = PROVIDER_NAME;
+        final java.security.KeyStore.LoadStoreParameter loadParameter;
         if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
-            providerName = "AndroidKeyStoreLegacy";
+            loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
+                    KeyProperties.legacyUidToNamespace(uid));
+        } else {
+            loadParameter = new AndroidKeyStoreLoadStoreParameter(uid);
         }
-        java.security.KeyStore result =
-                java.security.KeyStore.getInstance(providerName);
+        java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
         try {
-            result.load(new AndroidKeyStoreLoadStoreParameter(uid));
+            result.load(loadParameter);
         } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
             throw new KeyStoreException(
                     "Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index fa4f8b1..3ebca6a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
 import android.security.KeyStore;
 import android.security.keymaster.KeymasterDefs;
 
@@ -874,9 +876,18 @@
      * which it must be configured in SEPolicy.
      * @hide
      */
+    @SystemApi
     public static final int NAMESPACE_APPLICATION = -1;
 
     /**
+     * The namespace identifier for the WIFI Keystore namespace.
+     * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+     * @hide
+     */
+    @SystemApi
+    public static final int NAMESPACE_WIFI = 102;
+
+    /**
      * For legacy support, translate namespaces into known UIDs.
      * @hide
      */
@@ -884,6 +895,8 @@
         switch (namespace) {
             case NAMESPACE_APPLICATION:
                 return KeyStore.UID_SELF;
+            case NAMESPACE_WIFI:
+                return Process.WIFI_UID;
             // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
             //  b/171305388 and b/171305607
             default:
@@ -900,6 +913,8 @@
         switch (uid) {
             case KeyStore.UID_SELF:
                 return NAMESPACE_APPLICATION;
+            case Process.WIFI_UID:
+                return NAMESPACE_WIFI;
             // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
             //  b/171305388 and b/171305607
             default:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9..0f77749 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@
 
         List<KeyParameter> parameters = new ArrayList<>();
         parameters.add(KeyStore2ParameterUtils.makeEnum(
+                KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN
+        ));
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
                 KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
         ));
         parameters.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 32650ae..5619585 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@
 import android.system.keystore2.Authorization;
 import android.system.keystore2.Domain;
 import android.system.keystore2.KeyDescriptor;
-import android.util.Log;
 
 import java.security.Key;
 
@@ -127,15 +126,6 @@
             return false;
         }
 
-        // If the key ids are equal and the class matches all the other fields cannot differ
-        // unless we have a bug.
-        if (!mAlgorithm.equals(other.mAlgorithm)
-                || !mAuthorizations.equals(other.mAuthorizations)
-                || !mDescriptor.equals(other.mDescriptor)) {
-            Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
-                    + "differs.");
-            return false;
-        }
         return true;
     }
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6a92980..70e30d2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -585,6 +585,30 @@
                     mSpec.getKeyValidityForConsumptionEnd()
             ));
         }
+        if (mSpec.getCertificateNotAfter() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+                    mSpec.getCertificateNotAfter()
+            ));
+        }
+        if (mSpec.getCertificateNotBefore() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+                    mSpec.getCertificateNotBefore()
+            ));
+        }
+        if (mSpec.getCertificateSerialNumber() != null) {
+            params.add(KeyStore2ParameterUtils.makeBignum(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+                    mSpec.getCertificateSerialNumber()
+            ));
+        }
+        if (mSpec.getCertificateSubject() != null) {
+            params.add(KeyStore2ParameterUtils.makeBytes(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+                    mSpec.getCertificateSubject().getEncoded()
+            ));
+        }
 
         if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
             params.add(KeyStore2ParameterUtils.makeInt(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 75ac61a..e101115 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -352,14 +352,17 @@
         try {
             response = keyStore.getKeyEntry(descriptor);
         } catch (android.security.KeyStoreException e) {
-            if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
-                throw new KeyPermanentlyInvalidatedException(
-                        "User changed or deleted their auth credentials",
-                        e);
-            } else {
-                throw (UnrecoverableKeyException)
-                        new UnrecoverableKeyException("Failed to obtain information about key")
-                                .initCause(e);
+            switch (e.getErrorCode()) {
+                case ResponseCode.KEY_NOT_FOUND:
+                    return null;
+                case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+                    throw new KeyPermanentlyInvalidatedException(
+                            "User changed or deleted their auth credentials",
+                            e);
+                default:
+                    throw (UnrecoverableKeyException)
+                            new UnrecoverableKeyException("Failed to obtain information about key")
+                                    .initCause(e);
             }
         }
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc4..39607ae 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@
         try {
             response = mKeyStore.getKeyEntry(wrappingkey);
         } catch (android.security.KeyStoreException e) {
-            throw new KeyStoreException("Failed to load wrapping key.", e);
+            throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+                    + e.getErrorCode(), e);
         }
 
         KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d..dcdd7de 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@
 import android.security.keystore.UserAuthArgs;
 import android.system.keystore2.Authorization;
 
+import java.math.BigInteger;
 import java.security.ProviderException;
 import java.util.ArrayList;
 import java.util.Date;
@@ -154,6 +155,23 @@
     }
 
     /**
+     * This function constructs a {@link KeyParameter} expressing a Bignum.
+     * @param tag Must be KeyMint tag with the associated type BIGNUM.
+     * @param b A BitInteger to be stored in the new key parameter.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+            throw new IllegalArgumentException("Not a bignum tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.value = KeyParameterValue.blob(b.toByteArray());
+        return p;
+    }
+
+    /**
      * This function constructs a {@link KeyParameter} expressing date.
      * @param tag Must be KeyMint tag with the associated type DATE.
      * @param date A date
@@ -167,10 +185,6 @@
         KeyParameter p = new KeyParameter();
         p.tag = tag;
         p.value = KeyParameterValue.dateTime(date.getTime());
-        if (p.value.getDateTime() < 0) {
-            throw new IllegalArgumentException("Date tag value out of range: "
-                    + p.value.getDateTime());
-        }
         return p;
     }
     /**
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 93a6e7b..b581f55 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -97,14 +97,4 @@
         android:padding="@dimen/pip_resize_handle_padding"
         android:src="@drawable/pip_resize_handle"
         android:background="?android:selectableItemBackgroundBorderless" />
-
-    <!-- invisible layer to trap the focus, b/169372603 -->
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@null"
-        android:defaultFocusHighlightEnabled="false"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:focusedByDefault="true" />
-t </FrameLayout>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index fe97e24..982cc00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
 import java.util.Optional;
@@ -38,7 +39,7 @@
     private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
 
     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
-    private final Optional<SplitScreen> mSplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     private final Optional<Pip> mPipOptional;
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<HideDisplayCutout> mHideDisplayCutout;
@@ -50,7 +51,7 @@
     public static ShellCommandHandler create(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
@@ -64,7 +65,7 @@
     private ShellCommandHandlerImpl(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 0958a07..925bf4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -39,7 +40,7 @@
     private final DragAndDropController mDragAndDropController;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
-    private final Optional<SplitScreen> mSplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     private final Optional<AppPairs> mAppPairsOptional;
     private final FullscreenTaskListener mFullscreenTaskListener;
     private final ShellExecutor mMainExecutor;
@@ -51,7 +52,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -71,7 +72,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -97,7 +98,7 @@
         mShellTaskOrganizer.registerOrganizer();
 
         mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
-        mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
+        mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
 
         // Bind the splitscreen impl to the drag drop controller
         mDragAndDropController.initialize(mSplitScreenOptional);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.animation
 
-import android.os.Looper
 import android.util.ArrayMap
 import android.util.Log
 import android.view.View
@@ -847,7 +846,7 @@
      * pass to [spring].
      */
     data class SpringConfig internal constructor(
-        internal var stiffness: Float,
+        var stiffness: Float,
         internal var dampingRatio: Float,
         internal var startVelocity: Float = 0f,
         internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
      */
     data class FlingConfig internal constructor(
         internal var friction: Float,
-        internal var min: Float,
-        internal var max: Float,
+        var min: Float,
+        var max: Float,
         internal var startVelocity: Float
     ) {
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 40fdb97..0ee1f06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -398,6 +398,14 @@
         }
     }
 
+    @Override
+    public void setExpandedContentAlpha(float alpha) {
+        if (mExpandedView != null) {
+            mExpandedView.setAlpha(alpha);
+            mExpandedView.setTaskViewAlpha(alpha);
+        }
+    }
+
     /**
      * Set visibility of bubble in the expanded state.
      *
@@ -407,7 +415,7 @@
      * and setting {@code false} actually means rendering the expanded view in transparent.
      */
     @Override
-    public void setContentVisibility(boolean visibility) {
+    public void setTaskViewVisibility(boolean visibility) {
         if (mExpandedView != null) {
             mExpandedView.setContentVisibility(visibility);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 29458ef..2100428 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -85,6 +85,19 @@
     private boolean mImeVisible;
     private boolean mNeedsNewHeight;
 
+    /**
+     * Whether we want the TaskView's content to be visible (alpha = 1f). If
+     * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value
+     * until the animation ends.
+     */
+    private boolean mIsContentVisible = false;
+
+    /**
+     * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha
+     * changes from {@link #setContentVisibility} until the animation ends.
+     */
+    private boolean mIsAlphaAnimating = false;
+
     private int mMinHeight;
     private int mOverflowHeight;
     private int mSettingsIconHeight;
@@ -465,6 +478,29 @@
     }
 
     /**
+     * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls
+     * to {@link #setContentVisibility} will not be applied until this is set to false again.
+     */
+    void setAlphaAnimating(boolean animating) {
+        mIsAlphaAnimating = animating;
+
+        // If we're done animating, apply the correct
+        if (!animating) {
+            setContentVisibility(mIsContentVisible);
+        }
+    }
+
+    /**
+     * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not
+     * affect the TaskView since it uses a Surface.
+     */
+    void setTaskViewAlpha(float alpha) {
+        if (mTaskView != null) {
+            mTaskView.setAlpha(alpha);
+        }
+    }
+
+    /**
      * Set visibility of contents in the expanded state.
      *
      * @param visibility {@code true} if the contents should be visible on the screen.
@@ -477,16 +513,19 @@
             Log.d(TAG, "setContentVisibility: visibility=" + visibility
                     + " bubble=" + getBubbleKey());
         }
+        mIsContentVisible = visibility;
+
         final float alpha = visibility ? 1f : 0f;
 
         mPointerView.setAlpha(alpha);
-        if (mTaskView != null) {
+        if (mTaskView != null && !mIsAlphaAnimating) {
             mTaskView.setAlpha(alpha);
         }
     }
 
+
     @Nullable
-    View getTaskView() {
+    TaskView getTaskView() {
         return mTaskView;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 16cd3cf..51d63cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -167,8 +167,12 @@
         return dotPath
     }
 
-    override fun setContentVisibility(visible: Boolean) {
-        expandedView?.setContentVisibility(visible)
+    override fun setExpandedContentAlpha(alpha: Float) {
+        expandedView?.alpha = alpha
+    }
+
+    override fun setTaskViewVisibility(visible: Boolean) {
+        // Overflow does not have a TaskView.
     }
 
     override fun getIconView(): BadgedImageView? {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 39510e5..a3edc20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -43,7 +43,6 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Bundle;
-import android.os.Handler;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Choreographer;
@@ -123,6 +122,10 @@
     @VisibleForTesting
     static final int FLYOUT_HIDE_AFTER = 5000;
 
+    private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
+
+    private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+
     /**
      * How long to wait to animate the stack temporarily invisible after a drag/flyout hide
      * animation ends, if we are in fact temporarily invisible.
@@ -142,7 +145,7 @@
 
     private final PhysicsAnimator.SpringConfig mTranslateSpringConfig =
             new PhysicsAnimator.SpringConfig(
-                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+                    SpringForce.STIFFNESS_VERY_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
 
     /**
      * Handler to use for all delayed animations - this way, we can easily cancel them before
@@ -211,6 +214,9 @@
     /** Container for the animating-out SurfaceView. */
     private FrameLayout mAnimatingOutSurfaceContainer;
 
+    /** Animator for animating the alpha value of the animating out SurfaceView. */
+    private final ValueAnimator mAnimatingOutSurfaceAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
     /**
      * Buffer containing a screenshot of the animating-out bubble. This is drawn into the
      * SurfaceView during animations.
@@ -261,6 +267,12 @@
     /** Whether we're in the middle of dragging the stack around by touch. */
     private boolean mIsDraggingStack = false;
 
+    /** Whether the expanded view has been hidden, because we are dragging out a bubble. */
+    private boolean mExpandedViewHidden = false;
+
+    /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
+    private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
     /**
      * The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore
      * touches from other pointer indices.
@@ -614,6 +626,12 @@
             // Show the dismiss target, if we haven't already.
             mDismissView.show();
 
+            if (mIsExpanded && mExpandedBubble != null && v.equals(mExpandedBubble.getIconView())) {
+                // Hide the expanded view if we're dragging out the expanded bubble, and we haven't
+                // already hidden it.
+                hideExpandedViewIfNeeded();
+            }
+
             // First, see if the magnetized object consumes the event - if so, we shouldn't move the
             // bubble since it's stuck to the target.
             if (!passEventToMagnetizedObject(ev)) {
@@ -645,6 +663,9 @@
             if (!passEventToMagnetizedObject(ev)) {
                 if (mBubbleData.isExpanded()) {
                     mExpandedAnimationController.snapBubbleBack(v, velX, velY);
+
+                    // Re-show the expanded view if we hid it.
+                    showExpandedViewIfNeeded();
                 } else {
                     // Fling the stack to the edge, and save whether or not it's going to end up on
                     // the left side of the screen.
@@ -937,6 +958,46 @@
         animate()
                 .setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
                 .setDuration(FADE_IN_DURATION);
+
+        mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+        mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+        mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                    // We need to be Z ordered on top in order for alpha animations to work.
+                    mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
+                    mExpandedBubble.getExpandedView().setAlphaAnimating(true);
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                    mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+                    mExpandedBubble.getExpandedView().setAlphaAnimating(false);
+                }
+            }
+        });
+        mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
+            if (mExpandedBubble != null) {
+                mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue());
+            }
+        });
+
+        mAnimatingOutSurfaceAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+        mAnimatingOutSurfaceAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+        mAnimatingOutSurfaceAlphaAnimator.addUpdateListener(valueAnimator -> {
+            if (!mExpandedViewHidden) {
+                mAnimatingOutSurfaceView.setAlpha((float) valueAnimator.getAnimatedValue());
+            }
+        });
+        mAnimatingOutSurfaceAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                releaseAnimatingOutBubbleBuffer();
+            }
+        });
     }
 
     /**
@@ -1478,6 +1539,9 @@
      * Update bubble order and pointer position.
      */
     public void updateBubbleOrder(List<Bubble> bubbles) {
+        if (isExpansionAnimating()) {
+            return;
+        }
         final Runnable reorder = () -> {
             for (int i = 0; i < bubbles.size(); i++) {
                 Bubble bubble = bubbles.get(i);
@@ -1536,7 +1600,8 @@
 
         // If we're expanded, screenshot the currently expanded bubble (before expanding the newly
         // selected bubble) so we can animate it out.
-        if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+        if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null
+                && !mExpandedViewHidden) {
             if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
                 // Before screenshotting, have the real ActivityView show on top of other surfaces
                 // so that the screenshot doesn't flicker on top of it.
@@ -1572,7 +1637,7 @@
             mExpandedViewContainer.setAlpha(0.0f);
             mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
                 if (previouslySelected != null) {
-                    previouslySelected.setContentVisibility(false);
+                    previouslySelected.setTaskViewVisibility(false);
                 }
 
                 updateExpandedBubble();
@@ -1653,6 +1718,58 @@
         requestUpdate();
     }
 
+    /** Animate the expanded view hidden. This is done while we're dragging out a bubble. */
+    private void hideExpandedViewIfNeeded() {
+        if (mExpandedViewHidden
+                || mExpandedBubble == null
+                || mExpandedBubble.getExpandedView() == null) {
+            return;
+        }
+
+        mExpandedViewHidden = true;
+
+        // Scale down.
+        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+                .spring(AnimatableScaleMatrix.SCALE_X,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+                                1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+                        mScaleOutSpringConfig)
+                .spring(AnimatableScaleMatrix.SCALE_Y,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+                                1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+                        mScaleOutSpringConfig)
+                .addUpdateListener((target, values) ->
+                        mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+                .start();
+
+        // Animate alpha from 1f to 0f.
+        mExpandedViewAlphaAnimator.reverse();
+    }
+
+    /**
+     * Animate the expanded view visible again. This is done when we're done dragging out a bubble.
+     */
+    private void showExpandedViewIfNeeded() {
+        if (!mExpandedViewHidden) {
+            return;
+        }
+
+        mExpandedViewHidden = false;
+
+        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+                .spring(AnimatableScaleMatrix.SCALE_X,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+                        mScaleOutSpringConfig)
+                .spring(AnimatableScaleMatrix.SCALE_Y,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+                        mScaleOutSpringConfig)
+                .addUpdateListener((target, values) ->
+                        mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+                .start();
+
+        mExpandedViewAlphaAnimator.start();
+    }
+
     private void animateExpansion() {
         cancelDelayedExpandCollapseSwitchAnimations();
         final boolean showVertically = mPositioner.showBubblesVertically();
@@ -1662,6 +1779,7 @@
         }
         beforeExpandedViewAnimation();
 
+        updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
         mBubbleContainer.setActiveController(mExpandedAnimationController);
         updateOverflowVisibility();
         updatePointerPosition();
@@ -1710,7 +1828,7 @@
         // Should not happen since we lay out before expanding, but just in case...
         if (getWidth() > 0) {
             startDelay = (long)
-                    (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
+                    (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 1.2f
                             + (distanceAnimated / getWidth()) * 30);
         }
 
@@ -1724,20 +1842,29 @@
                 pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
             }
             mExpandedViewContainerMatrix.setScale(
-                    0f, 0f,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     pivotX, pivotY);
         } else {
             mExpandedViewContainerMatrix.setScale(
-                    0f, 0f,
-                    bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY());
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+                    bubbleWillBeAt + mBubbleSize / 2f,
+                    getExpandedViewY());
         }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
-        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-            mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+        if (mExpandedBubble.getExpandedView() != null) {
+            mExpandedBubble.setExpandedContentAlpha(0f);
+
+            // We'll be starting the alpha animation after a slight delay, so set this flag early
+            // here.
+            mExpandedBubble.getExpandedView().setAlphaAnimating(true);
         }
 
         mDelayedAnimationExecutor.executeDelayed(() -> {
+            mExpandedViewAlphaAnimator.start();
+
             PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
             PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
                     .spring(AnimatableScaleMatrix.SCALE_X,
@@ -1792,22 +1919,15 @@
         // since we're about to animate collapsed.
         mExpandedAnimationController.notifyPreparingToCollapse();
 
-        final long startDelay =
-                (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
-        mDelayedAnimationExecutor.executeDelayed(() -> {
-            mExpandedAnimationController.collapseBackToStack(
-                    mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
-                    /* collapseTo */,
-                    () -> mBubbleContainer.setActiveController(mStackAnimationController));
-        }, startDelay);
+        mExpandedAnimationController.collapseBackToStack(
+                mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
+                /* collapseTo */,
+                () -> mBubbleContainer.setActiveController(mStackAnimationController));
 
         if (mTaskbarScrim.getVisibility() == VISIBLE) {
             mTaskbarScrim.animate().alpha(0f).start();
         }
 
-        // We want to visually collapse into this bubble during the animation.
-        final View expandingFromBubble = mExpandedBubble.getIconView();
-
         int index;
         if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
             index = mBubbleData.getBubbles().size();
@@ -1836,31 +1956,25 @@
                     getExpandedViewY());
         }
 
+        mExpandedViewAlphaAnimator.reverse();
+
+        // When the animation completes, we should no longer be showing the content.
+        if (mExpandedBubble.getExpandedView() != null) {
+            mExpandedBubble.getExpandedView().setContentVisibility(false);
+        }
+
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
         PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
-                .spring(AnimatableScaleMatrix.SCALE_X, 0f, mScaleOutSpringConfig)
-                .spring(AnimatableScaleMatrix.SCALE_Y, 0f, mScaleOutSpringConfig)
+                .spring(AnimatableScaleMatrix.SCALE_X,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+                                1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+                        mScaleOutSpringConfig)
+                .spring(AnimatableScaleMatrix.SCALE_Y,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+                                1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+                        mScaleOutSpringConfig)
                 .addUpdateListener((target, values) -> {
-                    if (expandingFromBubble != null) {
-                        // Follow the bubble as it translates!
-                        if (showVertically) {
-                            mExpandedViewContainerMatrix.postTranslate(
-                                    0f, expandingFromBubble.getTranslationY()
-                                            - expandingFromBubbleAt);
-                        } else {
-                            mExpandedViewContainerMatrix.postTranslate(
-                                    expandingFromBubble.getTranslationX()
-                                            - expandingFromBubbleAt, 0f);
-                        }
-                    }
-
                     mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
-
-                    // Hide early so we don't have a tiny little expanded view still visible at the
-                    // end of the scale animation.
-                    if (mExpandedViewContainerMatrix.getScaleX() < 0.05f) {
-                        mExpandedViewContainer.setVisibility(View.INVISIBLE);
-                    }
                 })
                 .withEndActions(() -> {
                     final BubbleViewProvider previouslySelected = mExpandedBubble;
@@ -1875,10 +1989,10 @@
                                 mExpandedBubble));
                     }
                     updateOverflowVisibility();
-
+                    updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
                     afterExpandedViewAnimation();
                     if (previouslySelected != null) {
-                        previouslySelected.setContentVisibility(false);
+                        previouslySelected.setTaskViewVisibility(false);
                     }
 
                     if (mPositioner.showingInTaskbar()) {
@@ -1899,22 +2013,21 @@
         // The surface contains a screenshot of the animating out bubble, so we just need to animate
         // it out (and then release the GraphicBuffer).
         PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
-        PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
-                .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
-                .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
-                .withEndActions(this::releaseAnimatingOutBubbleBuffer);
+
+        mAnimatingOutSurfaceAlphaAnimator.reverse();
+        mExpandedViewAlphaAnimator.start();
 
         if (mPositioner.showBubblesVertically()) {
             float translationX = mStackAnimationController.isStackOnLeftSide()
                     ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
                     : mAnimatingOutSurfaceContainer.getTranslationX();
-            animator.spring(DynamicAnimation.TRANSLATION_X,
-                    translationX,
-                    mTranslateSpringConfig)
+            PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+                    .spring(DynamicAnimation.TRANSLATION_X, translationX, mTranslateSpringConfig)
                     .start();
         } else {
-            animator.spring(DynamicAnimation.TRANSLATION_Y,
-                    mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+            PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+                    .spring(DynamicAnimation.TRANSLATION_Y,
+                            mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize,
                     mTranslateSpringConfig)
                     .start();
         }
@@ -1943,7 +2056,8 @@
                     pivotX, pivotY);
         } else {
             mExpandedViewContainerMatrix.setScale(
-                    0f, 0f,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     expandingFromBubbleDestination + mBubbleSize / 2f,
                     getExpandedViewY());
         }
@@ -1968,10 +2082,7 @@
                         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
                     })
                     .withEndActions(() -> {
-                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
-                        }
-
+                        mExpandedViewHidden = false;
                         mIsBubbleSwitchAnimating = false;
                     })
                     .start();
@@ -2485,6 +2596,7 @@
                 && mExpandedBubble.getExpandedView() != null) {
             BubbleExpandedView bev = mExpandedBubble.getExpandedView();
             bev.setContentVisibility(false);
+            bev.setAlphaAnimating(!mIsExpansionAnimating);
             mExpandedViewContainerMatrix.setScaleX(0f);
             mExpandedViewContainerMatrix.setScaleY(0f);
             mExpandedViewContainerMatrix.setTranslate(0f, 0f);
@@ -2582,7 +2694,14 @@
                     mAnimatingOutBubbleBuffer.getHardwareBuffer(),
                     mAnimatingOutBubbleBuffer.getColorSpace());
 
-            mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true)));
+            mAnimatingOutSurfaceView.setAlpha(1f);
+            mExpandedViewContainer.setVisibility(View.GONE);
+
+            mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+                post(() -> {
+                    onComplete.accept(true);
+                });
+            });
         });
     }
 
@@ -2614,7 +2733,9 @@
             }
         }
         mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0);
-        mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+        if (mIsExpansionAnimating) {
+            mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+        }
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
             mExpandedViewContainer.setTranslationY(getExpandedViewY());
             mExpandedViewContainer.setTranslationX(0f);
@@ -2623,7 +2744,6 @@
         }
 
         mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
-        updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index ec900be..da4259c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -29,7 +29,16 @@
 public interface BubbleViewProvider {
     @Nullable BubbleExpandedView getExpandedView();
 
-    void setContentVisibility(boolean visible);
+    /**
+     * Sets the alpha of the expanded view content. This will be applied to both the expanded view
+     * container itself (the manage button, etc.) as well as the TaskView within it.
+     */
+    void setExpandedContentAlpha(float alpha);
+
+    /**
+     * Sets whether the contents of the bubble's TaskView should be visible.
+     */
+    void setTaskViewVisibility(boolean visible);
 
     @Nullable View getIconView();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 616f24a..fb70cbe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -40,9 +40,11 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
 import android.window.ClientWindowFrames;
@@ -251,6 +253,8 @@
     public class SysUiWindowManager extends WindowlessWindowManager {
         final int mDisplayId;
         ContainerWindow mContainerWindow;
+        final HashMap<IBinder, SurfaceControl> mLeashForWindow =
+                new HashMap<IBinder, SurfaceControl>();
         public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
                 ContainerWindow container) {
             super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
@@ -263,7 +267,33 @@
         }
 
         SurfaceControl getSurfaceControlForWindow(View rootView) {
-            return getSurfaceControl(rootView);
+            synchronized (this) {
+                return mLeashForWindow.get(getWindowBinder(rootView));
+            }
+        }
+
+        protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+            SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
+                  .setContainerLayer()
+                  .setName("SystemWindowLeash")
+                  .setHidden(false)
+                  .setParent(mRootSurface)
+                  .setCallsite("SysUiWIndowManager#attachToParentSurface").build();
+            synchronized (this) {
+                mLeashForWindow.put(window.asBinder(), leash);
+            }
+            b.setParent(leash);
+        }
+
+        @Override
+        public void remove(android.view.IWindow window) throws RemoteException {
+            super.remove(window);
+            synchronized(this) {
+                IBinder token = window.asBinder();
+                new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
+                    .apply();
+                mLeashForWindow.remove(token);
+            }
         }
 
         void setTouchableRegionForWindow(View rootView, Region region) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 7f9c34f..728f60d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -87,7 +87,7 @@
     }
 
     @Override
-    protected void attachToParentSurface(SurfaceControl.Builder b) {
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
         mParentContainerCallbacks.attachToParentSurface(b);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index c8938ad..1770943 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -53,6 +53,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Optional;
 
@@ -66,7 +67,7 @@
 
     private final Context mContext;
     private final DisplayController mDisplayController;
-    private SplitScreen mSplitScreen;
+    private SplitScreenController mSplitScreen;
 
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -76,7 +77,7 @@
         mDisplayController = displayController;
     }
 
-    public void initialize(Optional<SplitScreen> splitscreen) {
+    public void initialize(Optional<SplitScreenController> splitscreen) {
         mSplitScreen = splitscreen.orElse(null);
         mDisplayController.addDisplayWindowListener(this);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 35dcdd5..6f5f2eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -64,7 +64,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -81,18 +83,18 @@
     private final Context mContext;
     private final ActivityTaskManager mActivityTaskManager;
     private final Starter mStarter;
-    private final SplitScreen mSplitScreen;
+    private final SplitScreenController mSplitScreen;
     private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
 
     private DragSession mSession;
 
-    public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+    public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
         this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
     }
 
     @VisibleForTesting
     DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
-            SplitScreen splitScreen, Starter starter) {
+            SplitScreenController splitScreen, Starter starter) {
         mContext = context;
         mActivityTaskManager = activityTaskManager;
         mSplitScreen = splitScreen;
@@ -200,8 +202,8 @@
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
         final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
 
-        @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
-        @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+        @StageType int stage = STAGE_TYPE_UNDEFINED;
+        @StagePosition int position = STAGE_POSITION_UNDEFINED;
         if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
             // Update launch options for the split side we are targeting.
             position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -213,7 +215,28 @@
 
         final ClipDescription description = data.getDescription();
         final Intent dragData = mSession.dragData;
-        mStarter.startClipDescription(description, dragData, stage, position);
+        startClipDescription(description, dragData, stage, position);
+    }
+
+    private void startClipDescription(ClipDescription description, Intent intent,
+            @StageType int stage, @StagePosition int position) {
+        final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+        final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+        final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+                ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+        if (isTask) {
+            final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+            mStarter.startTask(taskId, stage, position, opts);
+        } else if (isShortcut) {
+            final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+            final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+            final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+            mStarter.startShortcut(packageName, id, stage, position, opts, user);
+        } else {
+            mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
+                    opts);
+        }
     }
 
     /**
@@ -267,34 +290,13 @@
     /**
      * Interface for actually committing the task launches.
      */
-    @VisibleForTesting
     public interface Starter {
-        default void startClipDescription(ClipDescription description, Intent intent,
-                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
-            final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
-            final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
-            final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
-                    ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
-
-            if (isTask) {
-                final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
-                startTask(taskId, stage, position, opts);
-            } else if (isShortcut) {
-                final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
-                final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
-                final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
-                startShortcut(packageName, id, stage, position, opts, user);
-            } else {
-                startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
-            }
-        }
-        void startTask(int taskId, @SplitScreen.StageType int stage,
-                @SplitScreen.StagePosition int position, @Nullable Bundle options);
-        void startShortcut(String packageName, String shortcutId,
-                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
-                @Nullable Bundle options, UserHandle user);
-        void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
-                @SplitScreen.StagePosition int position, @Nullable Bundle options);
+        void startTask(int taskId, @StageType int stage, @StagePosition int position,
+                @Nullable Bundle options);
+        void startShortcut(String packageName, String shortcutId, @StageType int stage,
+                @StagePosition int position, @Nullable Bundle options, UserHandle user);
+        void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+                @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
         void exitSplitScreen();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 82c4e44..b342336 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,7 +42,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.ArrayList;
 
@@ -61,7 +61,7 @@
     private boolean mIsShowing;
     private boolean mHasDropped;
 
-    public DragLayout(Context context, SplitScreen splitscreen) {
+    public DragLayout(Context context, SplitScreenController splitscreen) {
         super(context);
         mPolicy = new DragAndDropPolicy(context, splitscreen);
         mDisplayMargin = context.getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index e13a1db..a18d106 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -862,14 +862,6 @@
      * assigned to it.
      */
     private SurfaceControl getWindowSurfaceControl() {
-        final ViewRootImpl root = getViewRootImpl();
-        if (root == null) {
-            return null;
-        }
-        SurfaceControl out = root.getSurfaceControl();
-        if (out != null && out.isValid()) {
-            return out;
-        }
         return mWindowManager.mSystemWindows.getViewSurface(this);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 45aa387..5ffa988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -16,6 +16,9 @@
 
 package com.android.wm.shell.pip;
 
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.view.Choreographer;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayLayout;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@
      * leash bounds before transformation/any animation. This is so when we try to construct
      * the different transformation matrices for the animation, we are constructing this based off
      * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
+     *
+     * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
+     * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
+     * rotation change.
      */
     @VisibleForTesting
     public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
             Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
-            @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+            @PipAnimationController.TransitionDirection int direction, float startingAngle,
+            @Surface.Rotation int rotationDelta) {
         if (mCurrentAnimator == null) {
             mCurrentAnimator = setupPipTransitionAnimator(
                     PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
-                            endBounds, sourceHintRect, direction, 0 /* startingAngle */));
+                            endBounds, sourceHintRect, direction, 0 /* startingAngle */,
+                            rotationDelta));
         } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                 && mCurrentAnimator.isRunning()) {
             // If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@
             mCurrentAnimator.cancel();
             mCurrentAnimator = setupPipTransitionAnimator(
                     PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
-                            endBounds, sourceHintRect, direction, startingAngle));
+                            endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
         }
         return mCurrentAnimator;
     }
@@ -410,7 +421,8 @@
 
         static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
                 Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
-                @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+                @PipAnimationController.TransitionDirection int direction, float startingAngle,
+                @Surface.Rotation int rotationDelta) {
             // Just for simplicity we'll interpolate between the source rect hint insets and empty
             // insets to calculate the window crop
             final Rect initialSourceValue;
@@ -431,6 +443,16 @@
             }
             final Rect sourceInsets = new Rect(0, 0, 0, 0);
 
+            final Rect rotatedEndRect;
+            if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
+                // Rotate the end bounds according to the rotation delta because the display will
+                // be rotated to the same orientation.
+                rotatedEndRect = new Rect(endValue);
+                DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
+            } else {
+                rotatedEndRect = null;
+            }
+
             // construct new Rect instances in case they are recycled
             return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
                     endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@
                     final Rect base = getBaseValue();
                     final Rect start = getStartValue();
                     final Rect end = getEndValue();
+                    if (rotatedEndRect != null) {
+                        // Animate the bounds in a different orientation. It only happens when
+                        // leaving PiP to fullscreen.
+                        applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
+                        return;
+                    }
                     Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                     float angle = (1.0f - fraction) * startingAngle;
                     setCurrentValue(bounds);
@@ -469,6 +497,25 @@
                     tx.apply();
                 }
 
+                private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+                        float fraction, Rect start, Rect end, Rect rotatedEndRect) {
+                    final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
+                    setCurrentValue(bounds);
+                    final float degree, x, y;
+                    if (rotationDelta == ROTATION_90) {
+                        degree = 90 * fraction;
+                        x = fraction * (end.right - start.left) + start.left;
+                        y = fraction * (end.top - start.top) + start.top;
+                    } else {
+                        degree = -90 * fraction;
+                        x = fraction * (end.left - start.left) + start.left;
+                        y = fraction * (end.bottom - start.top) + start.top;
+                    }
+                    getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
+                            rotatedEndRect, degree, x, y);
+                    tx.apply();
+                }
+
                 @Override
                 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                     getSurfaceTransactionHelper()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a777a27..97aeda4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -127,6 +127,32 @@
     }
 
     /**
+     * Operates the rotation according to the given degrees and scale (setMatrix) according to the
+     * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
+            SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
+            float positionX, float positionY) {
+        mTmpDestinationRect.set(sourceBounds);
+        final int dw = destinationBounds.width();
+        final int dh = destinationBounds.height();
+        // Scale by the short side so there won't be empty area if the aspect ratio of source and
+        // destination are different.
+        final float scale = dw <= dh
+                ? (float) sourceBounds.width() / dw
+                : (float) sourceBounds.height() / dh;
+        // Inverse scale for crop to fit in screen coordinates.
+        mTmpDestinationRect.scale(1 / scale);
+        mTmpTransform.setRotate(degrees);
+        mTmpTransform.postScale(scale, scale);
+        mTmpTransform.postTranslate(positionX, positionY);
+        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+                .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
+        return this;
+    }
+
+    /**
      * Resets the scale (setMatrix) on a given transaction and leash if there's any
      *
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index fb83006..ad6f435 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -48,13 +48,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Rational;
 import android.view.Display;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
@@ -72,8 +71,6 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Consumer;
@@ -134,7 +131,6 @@
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
-    private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
@@ -192,6 +188,12 @@
     private boolean mWaitForFixedRotation;
 
     /**
+     * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+     * meaningful if {@link #mWaitForFixedRotation} is true.
+     */
+    private @Surface.Rotation int mNextRotation;
+
+    /**
      * If set to {@code true}, no entering PiP transition would be kicked off and most likely
      * it's due to the fact that Launcher is handling the transition directly when swiping
      * auto PiP-able Activity to home.
@@ -313,61 +315,40 @@
             return;
         }
 
-        final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
-        if (initialConfig == null) {
-            Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
-            return;
-        }
         mPipUiEventLoggerLogger.log(
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
-        final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
-                != mPipBoundsState.getDisplayLayout().rotation();
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
+        final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
         final int direction = syncWithSplitScreenBounds(destinationBounds)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
-        if (orientationDiffers) {
-            mState = State.EXITING_PIP;
-            // Send started callback though animation is ignored.
-            sendOnPipTransitionStarted(direction);
-            // Don't bother doing an animation if the display rotation differs or if it's in
-            // a non-supported windowing mode
-            applyWindowingModeChangeOnExit(wct, direction);
-            mTaskOrganizer.applyTransaction(wct);
-            // Send finished callback though animation is ignored.
-            sendOnPipTransitionFinished(direction);
-        } else {
-            final SurfaceControl.Transaction tx =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
-                    mPipBoundsState.getBounds());
-            tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
-            // We set to fullscreen here for now, but later it will be set to UNDEFINED for
-            // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
-            wct.setActivityWindowingMode(mToken,
-                    direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
-                    ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    : WINDOWING_MODE_FULLSCREEN);
-            wct.setBounds(mToken, destinationBounds);
-            wct.setBoundsChangeTransaction(mToken, tx);
-            mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
-                @Override
-                public void onTransactionReady(int id, SurfaceControl.Transaction t) {
-                    mMainExecutor.execute(() -> {
-                        t.apply();
-                        // Make sure to grab the latest source hint rect as it could have been
-                        // updated right after applying the windowing mode change.
-                        final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
-                                mPictureInPictureParams, destinationBounds);
-                        scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
-                                0 /* startingAngle */, sourceHintRect, direction,
-                                animationDurationMs, null /* updateBoundsCallback */);
-                        mState = State.EXITING_PIP;
-                    });
-                }
-            });
-        }
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
+        tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+        // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+        // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+        wct.setActivityWindowingMode(mToken,
+                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        : WINDOWING_MODE_FULLSCREEN);
+        wct.setBounds(mToken, destinationBounds);
+        wct.setBoundsChangeTransaction(mToken, tx);
+        mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+            @Override
+            public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+                mMainExecutor.execute(() -> {
+                    t.apply();
+                    // Make sure to grab the latest source hint rect as it could have been
+                    // updated right after applying the windowing mode change.
+                    final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                            mPictureInPictureParams, destinationBounds);
+                    scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+                            0 /* startingAngle */, sourceHintRect, direction,
+                            animationDurationMs, null /* updateBoundsCallback */);
+                    mState = State.EXITING_PIP;
+                });
+            }
+        });
     }
 
     private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -399,7 +380,6 @@
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
                 .start();
-        mInitialState.remove(mToken.asBinder());
         mState = State.EXITING_PIP;
     }
 
@@ -424,7 +404,6 @@
         mToken = mTaskInfo.token;
         mState = State.TASK_APPEARED;
         mLeash = leash;
-        mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
         setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
                 mTaskInfo.topActivityInfo);
@@ -606,6 +585,7 @@
 
     @Override
     public void onFixedRotationStarted(int displayId, int newRotation) {
+        mNextRotation = newRotation;
         mWaitForFixedRotation = true;
     }
 
@@ -645,7 +625,7 @@
                 mPipAnimationController.getCurrentAnimator();
         if (animator == null || !animator.isRunning()
                 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
-            if (mState.isInPip() && fromRotation) {
+            if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
                 // Update bounds state to final destination first. It's important to do this
                 // before finishing & cancelling the transition animation so that the MotionHelper
                 // bounds are synchronized to the destination bounds when the animation ends.
@@ -1052,11 +1032,14 @@
             Log.w(TAG, "Abort animation, invalid leash");
             return;
         }
+        final int rotationDelta = mWaitForFixedRotation
+                ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
+                : Surface.ROTATION_0;
         Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                 ? mPipBoundsState.getBounds() : currentBounds;
         mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
-                        sourceHintRect, direction, startingAngle)
+                        sourceHintRect, direction, startingAngle, rotationDelta)
                 .setTransitionDirection(direction)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(durationMs)
@@ -1101,11 +1084,6 @@
         pw.println(innerPrefix + "mState=" + mState);
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
-        pw.println(innerPrefix + "mInitialState:");
-        for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
-            pw.println(innerPrefix + "  binder=" + e.getKey()
-                    + " winConfig=" + e.getValue().windowConfiguration);
-        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 91e8c99..9b6909b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@
                             taskInfo.pictureInPictureParams, currentBounds);
             animator = mPipAnimationController.getAnimator(taskInfo, leash,
                     currentBounds, currentBounds, destinationBounds, sourceHintRect,
-                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             t.setAlpha(leash, 0f);
             t.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index a57eee8..ae53005 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -198,8 +198,7 @@
      * {@code null}), it will get the leash that the WindowlessWM has assigned to it.
      */
     public SurfaceControl getSurfaceControl() {
-        SurfaceControl sf = mPipMenuView.getWindowSurfaceControl();
-        return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView);
+        return mSystemWindows.getViewSurface(mPipMenuView);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 6d12752..3eeba6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -31,7 +31,6 @@
 
     private static final String TAG = "PipMenuIconsAlgorithm";
 
-    private boolean mFinishedLayout = false;
     protected ViewGroup mViewRoot;
     protected ViewGroup mTopEndContainer;
     protected View mDragHandle;
@@ -51,27 +50,16 @@
         mDragHandle = dragHandle;
         mSettingsButton = settingsButton;
         mDismissButton = dismissButton;
+
+        bindInitialViewState();
     }
 
     /**
      * Updates the position of the drag handle based on where the PIP window is on the screen.
      */
     public void onBoundsChanged(Rect bounds) {
-        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
-                || mSettingsButton == null || mDismissButton == null) {
-            Log.e(TAG, "One if the required views is null.");
-            return;
-        }
-
-        //We only need to calculate the layout once since it does not change.
-        if (!mFinishedLayout) {
-            mTopEndContainer.removeView(mSettingsButton);
-            mViewRoot.addView(mSettingsButton);
-
-            setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
-            setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
-            mFinishedLayout = true;
-        }
+        // On phones, the menu icons are always static and will never move based on the PIP window
+        // position. No need to do anything here.
     }
 
     /**
@@ -84,4 +72,22 @@
             v.setLayoutParams(params);
         }
     }
+
+    /** Calculate the initial state of the menu icons. Called when the menu is first created. */
+    private void bindInitialViewState() {
+        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+                || mSettingsButton == null || mDismissButton == null) {
+            Log.e(TAG, "One of the required views is null.");
+            return;
+        }
+        // The menu view layout starts out with the settings button aligned at the top|end of the
+        // view group next to the dismiss button. On phones, the settings button should be aligned
+        // to the top|start of the view, so move it to parent view group to then align it to the
+        // top|start of the menu.
+        mTopEndContainer.removeView(mSettingsButton);
+        mViewRoot.addView(mSettingsButton);
+
+        setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+        setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index b91ba34..53571ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -107,6 +107,7 @@
     private boolean mThresholdCrossed0;
     private boolean mThresholdCrossed1;
     private boolean mUsingPinchToZoom = false;
+    private float mAngle = 0;
     int mFirstIndex = -1;
     int mSecondIndex = -1;
 
@@ -420,18 +421,25 @@
                 float down1X = mDownSecondaryPoint.x;
                 float down1Y = mDownSecondaryPoint.y;
 
-                // Top right + Bottom left pinch to zoom.
-                if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY)
-                        || (down1X > focusX && down1Y < focusY
-                        && down0X < focusX && down0Y > focusY)) {
+                if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
+                    // Top right + Bottom left pinch to zoom.
                     mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
-                } else if ((down0X < focusX && down0Y < focusY
-                        && down1X > focusX && down1Y > focusY)
-                        || (down1X < focusX && down1Y < focusY
-                        && down0X > focusX && down0Y > focusY)) {
+                } else if (down1X > focusX && down1Y < focusY
+                        && down0X < focusX && down0Y > focusY) {
+                    // Top right + Bottom left pinch to zoom.
+                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                            mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
+                } else if (down0X < focusX && down0Y < focusY
+                        && down1X > focusX && down1Y > focusY) {
+                    // Top left + bottom right pinch to zoom.
                     mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
+                } else if (down1X < focusX && down1Y < focusY
+                        && down0X > focusX && down0Y > focusY) {
+                    // Top left + bottom right pinch to zoom.
+                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                            mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
                 }
 
                 mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
@@ -445,21 +453,26 @@
         }
     }
 
-    private float mAngle = 0;
-
-    private float calculateRotationAngle(int focusX, int focusY, float x0, float y0,
-            float x1, float y1, boolean positive) {
+    private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY,
+            float bottomX, float bottomY, boolean positive) {
 
         // The base angle is the angle formed by taking the angle between the center horizontal
         // and one of the corners.
-        double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY),
-                Math.abs(mLastResizeBounds.right - focusX)));
+        double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - pivotY),
+                Math.abs(mLastResizeBounds.right - pivotX)));
+
         double angle0 = mThresholdCrossed0
-                ? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX)))
-                : baseAngle;
-        double angle1 = mThresholdCrossed1
-                ? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX)))
-                : baseAngle;
+                ? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle;
+        double angle1 = mThresholdCrossed0
+                ? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle;
+
+
+        if (positive) {
+            angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180;
+        } else {
+            angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0;
+            angle1 = -angle1;
+        }
 
         // Calculate the percentage difference of [0, 90] compare to the base angle.
         double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 2c68092..7ca5693 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -33,6 +33,7 @@
 
 /**
  * Interface to engage split-screen feature.
+ * TODO: Figure out which of these are actually needed outside of the Shell
  */
 @ExternalThread
 public interface SplitScreen extends DragAndDropPolicy.Starter {
@@ -102,18 +103,13 @@
     void setSideStagePosition(@StagePosition int sideStagePosition);
     /** Hides the side-stage if it is currently visible. */
     void setSideStageVisibility(boolean visible);
-    default void enterSplitScreen(int taskId, boolean leftOrTop) {
-        moveToSideStage(taskId,
-                leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
-    }
+
     /** Removes the split-screen stages. */
     void exitSplitScreen();
+    /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+    void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
     /** Gets the stage bounds. */
     void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
-    /** Dumps current status of split-screen. */
-    void dump(@NonNull PrintWriter pw, String prefix);
-    /** Called when the shell organizer has been registered. */
-    void onOrganizerRegistered();
 
     void registerSplitScreenListener(SplitScreenListener listener);
     void unregisterSplitScreenListener(SplitScreenListener listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 18dd53b..b0167af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,13 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
@@ -35,7 +42,9 @@
 
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 
 import java.io.PrintWriter;
 
@@ -44,25 +53,33 @@
  * {@link SplitScreen}.
  * @see StageCoordinator
  */
-public class SplitScreenController implements SplitScreen {
+public class SplitScreenController implements DragAndDropPolicy.Starter {
     private static final String TAG = SplitScreenController.class.getSimpleName();
 
     private final ShellTaskOrganizer mTaskOrganizer;
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+    private final ShellExecutor mMainExecutor;
+    private final SplitScreenImpl mImpl = new SplitScreenImpl();
+
     private StageCoordinator mStageCoordinator;
 
     public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
+            RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+            ShellExecutor mainExecutor) {
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
         mRootTDAOrganizer = rootTDAOrganizer;
+        mMainExecutor = mainExecutor;
     }
 
-    @Override
+    public SplitScreen asSplitScreen() {
+        return mImpl;
+    }
+
     public void onOrganizerRegistered() {
         if (mStageCoordinator == null) {
             // TODO: Multi-display
@@ -71,13 +88,11 @@
         }
     }
 
-    @Override
     public boolean isSplitScreenVisible() {
         return mStageCoordinator.isSplitScreenVisible();
     }
 
-    @Override
-    public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
+    public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
         if (task == null) {
             throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -85,50 +100,50 @@
         return moveToSideStage(task, sideStagePosition);
     }
 
-    @Override
     public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
-            @StagePosition int sideStagePosition) {
+            @SplitScreen.StagePosition int sideStagePosition) {
         return mStageCoordinator.moveToSideStage(task, sideStagePosition);
     }
 
-    @Override
     public boolean removeFromSideStage(int taskId) {
         return mStageCoordinator.removeFromSideStage(taskId);
     }
 
-    @Override
-    public void setSideStagePosition(@StagePosition int sideStagePosition) {
+    public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
         mStageCoordinator.setSideStagePosition(sideStagePosition);
     }
 
-    @Override
     public void setSideStageVisibility(boolean visible) {
         mStageCoordinator.setSideStageVisibility(visible);
     }
 
-    @Override
+    public void enterSplitScreen(int taskId, boolean leftOrTop) {
+        moveToSideStage(taskId,
+                leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+    }
+
     public void exitSplitScreen() {
         mStageCoordinator.exitSplitScreen();
     }
 
-    @Override
+    public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+        mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
+    }
+
     public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
         mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
     }
 
-    @Override
-    public void registerSplitScreenListener(SplitScreenListener listener) {
+    public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
         mStageCoordinator.registerSplitScreenListener(listener);
     }
 
-    @Override
-    public void unregisterSplitScreenListener(SplitScreenListener listener) {
+    public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
         mStageCoordinator.unregisterSplitScreenListener(listener);
     }
 
-    @Override
-    public void startTask(int taskId,
-            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+    public void startTask(int taskId, @SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -138,9 +153,9 @@
         }
     }
 
-    @Override
-    public void startShortcut(String packageName, String shortcutId, @StageType int stage,
-            @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+    public void startShortcut(String packageName, String shortcutId,
+            @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+            @Nullable Bundle options, UserHandle user) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -153,9 +168,8 @@
         }
     }
 
-    @Override
-    public void startIntent(PendingIntent intent,
-            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+    public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -165,8 +179,8 @@
         }
     }
 
-    private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
-            @Nullable Bundle options) {
+    private Bundle resolveStartStage(@SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         switch (stage) {
             case STAGE_TYPE_UNDEFINED: {
                 // Use the stage of the specified position is valid.
@@ -216,7 +230,6 @@
         return options;
     }
 
-    @Override
     public void dump(@NonNull PrintWriter pw, String prefix) {
         pw.println(prefix + TAG);
         if (mStageCoordinator != null) {
@@ -224,4 +237,120 @@
         }
     }
 
+    private class SplitScreenImpl implements SplitScreen {
+        @Override
+        public boolean isSplitScreenVisible() {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.isSplitScreenVisible();
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean moveToSideStage(int taskId, int sideStagePosition) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+                int sideStagePosition) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean removeFromSideStage(int taskId) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.removeFromSideStage(taskId);
+            }, Boolean.class);
+        }
+
+        @Override
+        public void setSideStagePosition(int sideStagePosition) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.setSideStagePosition(sideStagePosition);
+            });
+        }
+
+        @Override
+        public void setSideStageVisibility(boolean visible) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.setSideStageVisibility(visible);
+            });
+        }
+
+        @Override
+        public void enterSplitScreen(int taskId, boolean leftOrTop) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
+            });
+        }
+
+        @Override
+        public void exitSplitScreen() {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.exitSplitScreen();
+            });
+        }
+
+        @Override
+        public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
+            });
+        }
+
+        @Override
+        public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+            try {
+                mMainExecutor.executeBlocking(() -> {
+                    SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
+                            outBottomOrRightBounds);
+                });
+            } catch (InterruptedException e) {
+                Slog.e(TAG, "Failed to get stage bounds in 2s");
+            }
+        }
+
+        @Override
+        public void registerSplitScreenListener(SplitScreenListener listener) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.registerSplitScreenListener(listener);
+            });
+        }
+
+        @Override
+        public void unregisterSplitScreenListener(SplitScreenListener listener) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.unregisterSplitScreenListener(listener);
+            });
+        }
+
+        @Override
+        public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startTask(taskId, stage, position, options);
+            });
+        }
+
+        @Override
+        public void startShortcut(String packageName, String shortcutId, int stage, int position,
+                @Nullable Bundle options, UserHandle user) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
+                        options, user);
+            });
+        }
+
+        @Override
+        public void startIntent(PendingIntent intent, int stage, int position,
+                @Nullable Bundle options) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startIntent(intent, stage, position, options);
+            });
+        }
+    }
+
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 176852b..2d4b77e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -79,6 +79,7 @@
     private DisplayAreaInfo mDisplayAreaInfo;
     private final Context mContext;
     private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+    private boolean mExitSplitScreenOnHide = true;
 
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -113,7 +114,7 @@
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitScreen.StagePosition int sideStagePosition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        mSideStagePosition = sideStagePosition;
+        setSideStagePosition(sideStagePosition);
         mMainStage.activate(getMainStageBounds(), wct);
         mSideStage.addTask(task, getSideStageBounds(), wct);
         mTaskOrganizer.applyTransaction(wct);
@@ -144,10 +145,14 @@
     }
 
     void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+        if (mSideStagePosition == sideStagePosition) return;
+
         mSideStagePosition = sideStagePosition;
         if (mSideStageListener.mVisible) {
             onStageVisibilityChanged(mSideStageListener);
         }
+
+        sendOnStagePositionChanged();
     }
 
     void setSideStageVisibility(boolean visible) {
@@ -162,6 +167,10 @@
         exitSplitScreen(null /* childrenToTop */);
     }
 
+    void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+        mExitSplitScreenOnHide = exitSplitScreenOnHide;
+    }
+
     private void exitSplitScreen(StageTaskListener childrenToTop) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
@@ -202,6 +211,14 @@
         mListeners.remove(listener);
     }
 
+    private void sendOnStagePositionChanged() {
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            final SplitScreen.SplitScreenListener l = mListeners.get(i);
+            l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+            l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+        }
+    }
+
     private void onStageChildTaskStatusChanged(
             StageListenerImpl stageListener, int taskId, boolean present) {
 
@@ -251,7 +268,7 @@
             }
         }
 
-        if (!mainStageVisible && !sideStageVisible) {
+        if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
             // Exit split-screen if both stage are not visible.
             // TODO: This is only a temporary request from UX and is likely to be removed soon...
             exitSplitScreen();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 8e24e0b..f374922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -377,12 +377,10 @@
         final int taskId = startingWindowInfo.taskInfo.taskId;
         final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
                 snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
-        mMainExecutor.execute(() -> {
-            mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
-            final StartingWindowRecord tView =
-                    new StartingWindowRecord(null/* decorView */, surface);
-            mStartingWindowRecords.put(taskId, tView);
-        });
+        mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+        final StartingWindowRecord tView =
+                new StartingWindowRecord(null/* decorView */, surface);
+        mStartingWindowRecords.put(taskId, tView);
     }
 
     /**
@@ -392,42 +390,40 @@
         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
             Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
         }
-        mMainExecutor.execute(() -> removeWindowSynced(taskId));
+        removeWindowSynced(taskId);
     }
 
     protected void postAddWindow(int taskId, IBinder appToken,
-            View view, WindowManager wm, WindowManager.LayoutParams params) {
-        mMainExecutor.execute(() -> {
-            boolean shouldSaveView = true;
-            try {
-                wm.addView(view, params);
-            } catch (WindowManager.BadTokenException e) {
-                // ignore
-                Slog.w(TAG, appToken + " already running, starting window not displayed. "
-                        + e.getMessage());
+        View view, WindowManager wm, WindowManager.LayoutParams params) {
+        boolean shouldSaveView = true;
+        try {
+            wm.addView(view, params);
+        } catch (WindowManager.BadTokenException e) {
+            // ignore
+            Slog.w(TAG, appToken + " already running, starting window not displayed. "
+                    + e.getMessage());
+            shouldSaveView = false;
+        } catch (RuntimeException e) {
+            // don't crash if something else bad happens, for example a
+            // failure loading resources because we are loading from an app
+            // on external storage that has been unmounted.
+            Slog.w(TAG, appToken + " failed creating starting window", e);
+            shouldSaveView = false;
+        } finally {
+            if (view != null && view.getParent() == null) {
+                Slog.w(TAG, "view not successfully added to wm, removing view");
+                wm.removeViewImmediate(view);
                 shouldSaveView = false;
-            } catch (RuntimeException e) {
-                // don't crash if something else bad happens, for example a
-                // failure loading resources because we are loading from an app
-                // on external storage that has been unmounted.
-                Slog.w(TAG, appToken + " failed creating starting window", e);
-                shouldSaveView = false;
-            } finally {
-                if (view != null && view.getParent() == null) {
-                    Slog.w(TAG, "view not successfully added to wm, removing view");
-                    wm.removeViewImmediate(view);
-                    shouldSaveView = false;
-                }
             }
+        }
 
-            if (shouldSaveView) {
-                removeWindowSynced(taskId);
-                mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
-                final StartingWindowRecord tView =
-                        new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
-                mStartingWindowRecords.put(taskId, tView);
-            }
-        });
+        if (shouldSaveView) {
+            removeWindowSynced(taskId);
+            mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+            final StartingWindowRecord tView =
+                    new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
+            mStartingWindowRecords.put(taskId, tView);
+        }
     }
 
     protected void removeWindowSynced(int taskId) {
@@ -445,7 +441,7 @@
                 if (DEBUG_TASK_SNAPSHOT) {
                     Slog.v(TAG, "Removing task snapshot window for " + taskId);
                 }
-                record.mTaskSnapshotWindow.remove(mMainExecutor);
+                record.mTaskSnapshotWindow.remove();
             }
             mStartingWindowRecords.remove(taskId);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index b5e1896..a6f44ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -82,6 +82,7 @@
 import com.android.internal.policy.DecorView;
 import com.android.internal.view.BaseIWindow;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
 
 /**
  * This class represents a starting window that shows a snapshot.
@@ -121,6 +122,7 @@
     private final Window mWindow;
     private final Surface mSurface;
     private final Runnable mClearWindowHandler;
+    private final ShellExecutor mMainExecutor;
     private SurfaceControl mSurfaceControl;
     private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
@@ -213,7 +215,7 @@
         final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
                 windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
-                topWindowInsetsState, clearWindowHandler);
+                topWindowInsetsState, clearWindowHandler, mainExecutor);
         final Window window = snapshotSurface.mWindow;
 
         final InsetsState mTmpInsetsState = new InsetsState();
@@ -229,7 +231,7 @@
             } catch (RemoteException e) {
                 snapshotSurface.clearWindowSynced();
             }
-            window.setOuter(snapshotSurface, mainExecutor);
+            window.setOuter(snapshotSurface);
             try {
                 session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                         tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
@@ -249,7 +251,8 @@
             TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
             int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
             int currentOrientation, int activityType, InsetsState topWindowInsetsState,
-            Runnable clearWindowHandler) {
+            Runnable clearWindowHandler, ShellExecutor mainExecutor) {
+        mMainExecutor = mainExecutor;
         mSurface = new Surface();
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = new Window();
@@ -286,28 +289,26 @@
         mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
     }
 
-    void remove(ShellExecutor mainExecutor) {
+    void remove() {
         final long now = SystemClock.uptimeMillis();
         if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
                 // Show the latest content as soon as possible for unlocking to home.
                 && mActivityType != ACTIVITY_TYPE_HOME) {
             final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now;
-            mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime);
+            mMainExecutor.executeDelayed(() -> remove(), delayTime);
             if (DEBUG) {
                 Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
             }
             return;
         }
-        mainExecutor.execute(() -> {
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
-                }
-                mSession.remove(mWindow);
-            } catch (RemoteException e) {
-                // nothing
+        try {
+            if (DEBUG) {
+                Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
             }
-        });
+            mSession.remove(mWindow);
+        } catch (RemoteException e) {
+            // nothing
+        }
     }
 
     /**
@@ -497,13 +498,12 @@
         }
     }
 
+    @ExternalThread
     static class Window extends BaseIWindow {
         private TaskSnapshotWindow mOuter;
-        private ShellExecutor mMainExecutor;
 
-        public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) {
+        public void setOuter(TaskSnapshotWindow outer) {
             mOuter = outer;
-            mMainExecutor = mainExecutor;
         }
 
         @Override
@@ -511,22 +511,20 @@
                 MergedConfiguration mergedConfiguration, boolean forceLayout,
                 boolean alwaysConsumeSystemBars, int displayId) {
             if (mOuter != null) {
-                if (mergedConfiguration != null
-                        && mOuter.mOrientationOnCreation
-                        != mergedConfiguration.getMergedConfiguration().orientation) {
-                    // The orientation of the screen is changing. We better remove the snapshot ASAP
-                    // as we are going to wait on the new window in any case to unfreeze the screen,
-                    // and the starting window is not needed anymore.
-                    mMainExecutor.execute(() -> {
+                mOuter.mMainExecutor.execute(() -> {
+                    if (mergedConfiguration != null
+                            && mOuter.mOrientationOnCreation
+                            != mergedConfiguration.getMergedConfiguration().orientation) {
+                        // The orientation of the screen is changing. We better remove the snapshot
+                        // ASAP as we are going to wait on the new window in any case to unfreeze
+                        // the screen, and the starting window is not needed anymore.
                         mOuter.clearWindowSynced();
-                    });
-                } else if (reportDraw) {
-                    mMainExecutor.execute(() -> {
+                    } else if (reportDraw) {
                         if (mOuter.mHasDrawn) {
                             mOuter.reportDrawn();
                         }
-                    });
-                }
+                    }
+                });
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
new file mode 100644
index 0000000..85bbf74
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
@@ -0,0 +1,40 @@
+/*
+ * 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.wm.shell.transition;
+
+import android.annotation.NonNull;
+import android.window.IRemoteTransition;
+import android.window.TransitionFilter;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to manage remote transitions.
+ */
+@ExternalThread
+public interface RemoteTransitions {
+    /**
+     * Registers a remote transition.
+     */
+    void registerRemote(@NonNull TransitionFilter filter,
+            @NonNull IRemoteTransition remoteTransition);
+
+    /**
+     * Unregisters a remote transition.
+     */
+    void unregisterRemote(@NonNull IRemoteTransition remoteTransition);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d8687bd..1034489 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -67,6 +67,7 @@
     private final ShellExecutor mAnimExecutor;
     private final TransitionPlayerImpl mPlayerImpl;
     private final RemoteTransitionHandler mRemoteTransitionHandler;
+    private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
 
     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -78,6 +79,10 @@
     /** Keeps track of currently tracked transitions and all the animations associated with each */
     private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
 
+    public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
+        return transitions.mImpl;
+    }
+
     public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
             @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
         mOrganizer = organizer;
@@ -101,8 +106,20 @@
 
     /** Create an empty/non-registering transitions object for system-ui tests. */
     @VisibleForTesting
-    public static Transitions createEmptyForTesting() {
-        return new Transitions();
+    public static RemoteTransitions createEmptyForTesting() {
+        return new RemoteTransitions() {
+            @Override
+            public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
+                    @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+                // Do nothing
+            }
+
+            @Override
+            public void unregisterRemote(
+                    @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+                // Do nothing
+            }
+        };
     }
 
     /** Register this transition handler with Core */
@@ -134,16 +151,14 @@
     }
 
     /** Register a remote transition to be used when `filter` matches an incoming transition */
-    @ExternalThread
     public void registerRemote(@NonNull TransitionFilter filter,
             @NonNull IRemoteTransition remoteTransition) {
-        mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
+        mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
     }
 
     /** Unregisters a remote transition and all associated filters */
-    @ExternalThread
     public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
-        mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
+        mRemoteTransitionHandler.removeFiltered(remoteTransition);
     }
 
     /** @return true if the transition was triggered by opening something vs closing something */
@@ -371,4 +386,22 @@
             mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
         }
     }
+
+    @ExternalThread
+    private class RemoteTransitionImpl implements RemoteTransitions {
+        @Override
+        public void registerRemote(@NonNull TransitionFilter filter,
+                @NonNull IRemoteTransition remoteTransition) {
+            mMainExecutor.execute(() -> {
+                Transitions.this.registerRemote(filter, remoteTransition);
+            });
+        }
+
+        @Override
+        public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
+            mMainExecutor.execute(() -> {
+                Transitions.this.unregisterRemote(remoteTransition);
+            });
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 8258630..f06d57c 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -25,8 +25,6 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.wm.shell.flicker"/>
-        <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
-        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
         <option name="hidden-api-checks" value="false" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 7ad7553..3282ece 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Region
 import android.view.Surface
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
 import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -25,6 +26,109 @@
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
 
 @JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) {
+    end("appPairsDividerIsVisible", bugId) {
+        this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) {
+    end("appPairsDividerIsInVisible", bugId) {
+        this.notExists(APP_PAIR_SPLIT_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) {
+    all("dividerLayerBecomesVisible", bugId) {
+        this.hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) {
+    end("dockedStackDividerIsVisible", bugId) {
+        this.isVisible(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) {
+    all("dividerLayerBecomesVisible", bugId) {
+        this.hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) {
+    all("dividerLayerBecomesInvisible", bugId) {
+        this.showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .hidesLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) {
+    end("dockedStackDividerIsInvisible", bugId) {
+        this.notExists(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+    rotation: Int,
+    primaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("PrimaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+    rotation: Int,
+    secondaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("SecondaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+    rotation: Int,
+    primaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("PrimaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+    rotation: Int,
+    secondaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("SecondaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
 fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
@@ -51,8 +155,8 @@
 ) {
     all("dividerLayerBecomesVisible", bugId, enabled) {
         this.hidesLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
@@ -73,8 +177,8 @@
 ) {
     all("dividerLayerBecomesVisible", bugId, enabled) {
         this.hidesLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
@@ -85,8 +189,8 @@
 ) {
     all("dividerLayerBecomesInvisible", bugId, enabled) {
         this.showsLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .hidesLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
new file mode 100644
index 0000000..1869d83
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("Utils")
+package com.android.wm.shell.flicker
+
+import android.app.ActivityTaskManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
+import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+
+fun removeAllTasksButHome() {
+    val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
+        ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+        ACTIVITY_TYPE_UNDEFINED)
+    val atm = ActivityTaskManager.getService()
+    atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index d257749..c3fd663 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import android.os.SystemClock
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -40,7 +39,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -51,10 +49,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_cannotPairNonResizeableApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     nonResizeableApp?.launchViaIntent(wmHelper)
@@ -64,17 +61,20 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsInvisible()
-                    }
-                    windowManagerTrace {
-                        end("onlyResizeableAppWindowVisible") {
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsInvisible()
+                        }
+                        windowManagerTrace {
                             val nonResizeableApp = nonResizeableApp
                             require(nonResizeableApp != null) {
                                 "Non resizeable app not initialized"
                             }
-                            isVisible(nonResizeableApp.defaultWindowName)
-                            isInvisible(primaryApp.defaultWindowName)
+
+                            end("onlyResizeableAppWindowVisible") {
+                                isVisible(nonResizeableApp.defaultWindowName)
+                                isInvisible(primaryApp.defaultWindowName)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 257350b..7a2a5e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_pairPrimaryAndSecondaryApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     // TODO pair apps through normal UX flow
@@ -61,20 +58,27 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsVisible()
-                        end("appsEndingBounds", enabled = false) {
-                            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-                            this.hasVisibleRegion(primaryApp.defaultWindowName,
-                                appPairsHelper.getPrimaryBounds(dividerRegion))
-                                .hasVisibleRegion(secondaryApp.defaultWindowName,
-                                    appPairsHelper.getSecondaryBounds(dividerRegion))
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsVisible()
+                        }
+                        windowManagerTrace {
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                isVisible(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
-                    windowManagerTrace {
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                            isVisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            end("appsEndingBounds") {
+                                val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+                                this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                    appPairsHelper.getPrimaryBounds(dividerRegion))
+                                    .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 0b001f5..d8dc4c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 setup {
                     executeShellCommand(
@@ -66,24 +63,31 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsInvisible()
-                        start("appsStartingBounds", enabled = false) {
-                            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-                            this.hasVisibleRegion(primaryApp.defaultWindowName,
-                                appPairsHelper.getPrimaryBounds(dividerRegion))
-                                .hasVisibleRegion(secondaryApp.defaultWindowName,
-                                    appPairsHelper.getSecondaryBounds(dividerRegion))
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsInvisible()
                         }
-                        end("appsEndingBounds", enabled = false) {
-                            this.notExists(primaryApp.defaultWindowName)
-                                .notExists(secondaryApp.defaultWindowName)
+                        windowManagerTrace {
+                            end("bothAppWindowsInvisible") {
+                                isInvisible(primaryApp.defaultWindowName)
+                                isInvisible(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
-                    windowManagerTrace {
-                        end("bothAppWindowsInvisible") {
-                            isInvisible(primaryApp.defaultWindowName)
-                            isInvisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            start("appsStartingBounds") {
+                                val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+                                this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                    appPairsHelper.getPrimaryBounds(dividerRegion))
+                                    .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                            }
+                            end("appsEndingBounds") {
+                                this.notExists(primaryApp.defaultWindowName)
+                                    .notExists(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index aafa9bf..8aee005 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -46,7 +45,6 @@
  * Test open apps to app pairs and rotate.
  * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -60,7 +58,7 @@
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     executeShellCommand(composePairsCommand(
@@ -69,23 +67,28 @@
                     setRotation(configuration.endRotation)
                 }
                 assertions {
-                    layersTrace {
-                        navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = false)
-                        statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = false)
-                        appPairsDividerIsVisible(enabled = false)
-                        appPairsPrimaryBoundsIsVisible(configuration.endRotation,
-                            primaryApp.defaultWindowName, bugId = 172776659)
-                        appPairsSecondaryBoundsIsVisible(configuration.endRotation,
-                            secondaryApp.defaultWindowName, bugId = 172776659)
+                    presubmit {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                    .isVisible(secondaryApp.defaultWindowName)
+                            }
+                        }
                     }
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                                .isVisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            appPairsDividerIsVisible()
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+                                primaryApp.defaultWindowName, bugId = 172776659)
+                            appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+                                secondaryApp.defaultWindowName, bugId = 172776659)
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 19ca31f..bc99c94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -62,7 +62,7 @@
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag("testRotateAndEnterAppPairsMode", configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     this.setRotation(configuration.endRotation)
@@ -71,22 +71,37 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = !configuration.startRotation.isRotated())
-                        statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
-                        appPairsDividerIsVisible()
-                        appPairsPrimaryBoundsIsVisible(configuration.endRotation,
-                            primaryApp.defaultWindowName, 172776659)
-                        appPairsSecondaryBoundsIsVisible(configuration.endRotation,
-                            secondaryApp.defaultWindowName, 172776659)
+                    val isRotated = configuration.startRotation.isRotated()
+                    presubmit {
+                        layersTrace {
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            appPairsDividerIsVisible()
+                            if (!isRotated) {
+                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                    configuration.endRotation)
+                            }
+                        }
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                isVisible(secondaryApp.defaultWindowName)
+                            }
+                        }
                     }
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                            isVisible(secondaryApp.defaultWindowName)
+                    flaky {
+                        layersTrace {
+                            appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+                                primaryApp.defaultWindowName, 172776659)
+                            appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+                                secondaryApp.defaultWindowName, 172776659)
+
+                            if (isRotated) {
+                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                    configuration.endRotation)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 7ec22bb..cac46fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -32,13 +32,12 @@
     /**
      * Opens the IME and wait for it to be displayed
      *
-     * @param device UIDevice instance to interact with the device
      * @param wmHelper Helper used to wait for WindowManager states
      */
     @JvmOverloads
-    open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+    open fun openIME(wmHelper: WindowManagerStateHelper? = null) {
         if (!isTelevision) {
-            val editText = device.wait(
+            val editText = uiDevice.wait(
                 Until.findObject(By.res(getPackage(), "plain_text_input")),
                 FIND_TIMEOUT)
 
@@ -47,7 +46,7 @@
                     "was left in an unknown state (e.g. in split screen)"
             }
             editText.click()
-            waitAndAssertIMEShown(device, wmHelper)
+            waitAndAssertIMEShown(uiDevice, wmHelper)
         } else {
             // If we do the same thing as above - editText.click() - on TV, that's going to force TV
             // into the touch mode. We really don't want that.
@@ -69,16 +68,15 @@
     /**
      * Opens the IME and wait for it to be gone
      *
-     * @param device UIDevice instance to interact with the device
      * @param wmHelper Helper used to wait for WindowManager states
      */
     @JvmOverloads
-    open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+    open fun closeIME(wmHelper: WindowManagerStateHelper? = null) {
         if (!isTelevision) {
-            device.pressBack()
+            uiDevice.pressBack()
             // Using only the AccessibilityInfo it is not possible to identify if the IME is active
             if (wmHelper == null) {
-                device.waitForIdle()
+                uiDevice.waitForIdle()
             } else {
                 require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index b90e865..111362a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,17 +17,20 @@
 package com.android.wm.shell.flicker.helpers
 
 import android.app.Instrumentation
+import android.graphics.Point
 import android.media.session.MediaController
 import android.media.session.MediaSessionManager
 import android.os.SystemClock
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.BySelector
+import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
 import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
 import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
+import com.android.wm.shell.flicker.pip.waitPipWindowGone
+import com.android.wm.shell.flicker.pip.waitPipWindowShown
 import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assert.fail
 
 class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
     instrumentation,
@@ -55,6 +58,17 @@
         }
     }
 
+    /** {@inheritDoc}  */
+    override fun launchViaIntent(
+        wmHelper: WindowManagerStateHelper,
+        expectedWindowName: String,
+        action: String?,
+        stringExtras: Map<String, String>
+    ) {
+        super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
+        wmHelper.waitPipWindowShown()
+    }
+
     private fun focusOnObject(selector: BySelector): Boolean {
         // We expect all the focusable UI elements to be arranged in a way so that it is possible
         // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
@@ -69,16 +83,12 @@
         return false
     }
 
-    fun clickEnterPipButton() {
+    @JvmOverloads
+    fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) {
         clickObject(ENTER_PIP_BUTTON_ID)
 
-        // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
-        if (!isTelevision) {
-            uiDevice.hasPipWindow()
-        } else {
-            // Simply wait for 3 seconds
-            SystemClock.sleep(3_000)
-        }
+        // Wait on WMHelper or simply wait for 3 seconds
+        wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000)
     }
 
     fun clickStartMediaSessionButton() {
@@ -97,16 +107,75 @@
     fun stopMedia() = mediaController?.transportControls?.stop()
             ?: error("No active media session found")
 
+    @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead",
+        ReplaceWith("closePipWindow(wmHelper)"))
     fun closePipWindow() {
         if (isTelevision) {
             uiDevice.closeTvPipWindow()
         } else {
             uiDevice.closePipWindow()
         }
+    }
 
-        if (!waitUntilClosed()) {
-            fail("Couldn't close Pip")
+    /**
+     * Expands the pip window and dismisses it by clicking on the X button.
+     *
+     * Note, currently the View coordinates reported by the accessibility are relative to
+     * the window, so the correct coordinates need to be calculated
+     *
+     * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the
+     * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in
+     * Point(716, 66), instead of Point(970, 1403)
+     *
+     * See b/179337864
+     */
+    fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+        if (isTelevision) {
+            uiDevice.closeTvPipWindow()
+        } else {
+            expandPipWindow(wmHelper)
+            val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+            requireNotNull(exitPipObject) { "PIP window dismiss button not found" }
+            val coordinatesInWindow = exitPipObject.visibleBounds
+            val windowOffset = wmHelper.getWindowRegion(component).bounds
+            val newCoordinates = Point(windowOffset.left + coordinatesInWindow.centerX(),
+                windowOffset.top + coordinatesInWindow.centerY())
+            uiDevice.click(newCoordinates.x, newCoordinates.y)
         }
+
+        // Wait for animation to complete.
+        wmHelper.waitPipWindowGone()
+        wmHelper.waitForHomeActivityVisible()
+    }
+
+    /**
+     * Click once on the PIP window to expand it
+     */
+    fun expandPipWindow(wmHelper: WindowManagerStateHelper) {
+        val windowRegion = wmHelper.getWindowRegion(component)
+        require(!windowRegion.isEmpty) {
+            "Unable to find a PIP window in the current state"
+        }
+        val windowRect = windowRegion.bounds
+        uiDevice.click(windowRect.centerX(), windowRect.centerY())
+        // Ensure WindowManagerService wait until all animations have completed
+        wmHelper.waitForAppTransitionIdle()
+        mInstrumentation.uiAutomation.syncInputTransactions()
+    }
+
+    /**
+     * Double click on the PIP window to reopen to app
+     */
+    fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
+        val windowRegion = wmHelper.getWindowRegion(component)
+        require(!windowRegion.isEmpty) {
+            "Unable to find a PIP window in the current state"
+        }
+        val windowRect = windowRegion.bounds
+        uiDevice.click(windowRect.centerX(), windowRect.centerY())
+        uiDevice.click(windowRect.centerX(), windowRect.centerY())
+        wmHelper.waitPipWindowGone()
+        wmHelper.waitForAppTransitionIdle()
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
index 2015f49..bc42d5e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
@@ -16,11 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.app.ActivityTaskManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
-import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
 import android.os.SystemClock
 import com.android.wm.shell.flicker.NonRotationTestBase
 
@@ -29,14 +24,6 @@
     rotation: Int
 ) : NonRotationTestBase(rotationName, rotation) {
     companion object {
-        fun removeAllTasksButHome() {
-            val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
-                    ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
-                    ACTIVITY_TYPE_UNDEFINED)
-            val atm = ActivityTaskManager.getService()
-            atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
-        }
-
         fun waitForAnimationComplete() {
             // TODO: UiDevice doesn't have reliable way to wait for the completion of animation.
             // Consider to introduce WindowManagerStateHelper to access Activity state.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 5a3d18d..a14b46e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,21 +16,21 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,82 +39,67 @@
  * Test Pip launch and exit.
  * To run this test: `atest WMShellFlickerTests:EnterExitPipTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class EnterExitPipTest(
-    rotationName: String,
-    rotation: Int
-) : AppTestBase(rotationName, rotation) {
-    private val pipApp = PipAppHelper(instrumentation)
-    private val testApp = FixedAppHelper(instrumentation)
-
-    @Test
-    fun testDisplayMetricsPinUnpin() {
-        runFlicker(instrumentation) {
-            withTestName { "testDisplayMetricsPinUnpin" }
-            setup {
-                test {
-                    removeAllTasksButHome()
-                    device.wakeUpAndGoToHomeScreen()
-                    pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"))
-                    testApp.launchViaIntent()
-                    waitForAnimationComplete()
-                }
-            }
-            transitions {
-                // This will bring PipApp to fullscreen
-                pipApp.launchViaIntent()
-                waitForAnimationComplete()
-            }
-            teardown {
-                test {
-                    removeAllTasksButHome()
-                }
-            }
-            assertions {
-                val displayBounds = WindowUtils.getDisplayBounds(rotation)
-                windowManagerTrace {
-                    all("pipApp must remain inside visible bounds") {
-                        coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
-                    }
-                    all("Initially shows both app windows then pipApp hides testApp") {
-                        showsAppWindow(testApp.defaultWindowName)
-                                .showsAppWindowOnTop(pipApp.defaultWindowName)
-                                .then()
-                                .hidesAppWindow(testApp.defaultWindowName)
-                    }
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-                layersTrace {
-                    all("Initially shows both app layers then pipApp hides testApp") {
-                        showsLayer(testApp.defaultWindowName)
-                                .showsLayer(pipApp.defaultWindowName)
-                                .then()
-                                .hidesLayer(testApp.defaultWindowName)
-                    }
-                    start("testApp covers the fullscreen, pipApp remains inside display") {
-                        hasVisibleRegion(testApp.defaultWindowName, displayBounds)
-                        coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
-                    }
-                    end("pipApp covers the fullscreen") {
-                        hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
-                    }
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                }
-            }
-        }
-    }
-
-    companion object {
+    testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        fun getParams(): List<Array<Any>> {
+            val testApp = FixedAppHelper(instrumentation)
+            val baseConfig = getTransitionLaunch(eachRun = true)
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                setup {
+                    eachRun {
+                        testApp.launchViaIntent(wmHelper)
+                    }
+                }
+                transitions {
+                    // This will bring PipApp to fullscreen
+                    pipApp.launchViaIntent(wmHelper)
+                }
+                assertions {
+                    val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+                    presubmit {
+                        windowManagerTrace {
+                            all("pipApp must remain inside visible bounds") {
+                                coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+                            }
+                            all("Initially shows both app windows then pipApp hides testApp") {
+                                showsAppWindow(testApp.defaultWindowName)
+                                    .showsAppWindowOnTop(pipApp.defaultWindowName)
+                                    .then()
+                                    .hidesAppWindow(testApp.defaultWindowName)
+                            }
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+                        layersTrace {
+                            all("Initially shows both app layers then pipApp hides testApp") {
+                                showsLayer(testApp.defaultWindowName)
+                                    .showsLayer(pipApp.defaultWindowName)
+                                    .then()
+                                    .hidesLayer(testApp.defaultWindowName)
+                            }
+                            start("testApp covers the fullscreen, pipApp remains inside display") {
+                                hasVisibleRegion(testApp.defaultWindowName, displayBounds)
+                                coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+                            }
+                            end("pipApp covers the fullscreen") {
+                                hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+                            }
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                        }
+                    }
+                }
+            }
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+                testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+                repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index af62eb9..99a40da 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,18 +16,13 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.os.Bundle
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
 import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
@@ -35,9 +30,7 @@
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.PipAppHelper
 import org.junit.FixMethodOrder
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -50,80 +43,58 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
 class EnterPipTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
-    companion object {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val instrumentation = InstrumentationRegistry.getInstrumentation()
-            val testApp = PipAppHelper(instrumentation)
-            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
-                supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
-                    withTestName { buildTestTag("enterPip", testApp, configuration) }
-                    repeat { configuration.repetitions }
-                    setup {
-                        test {
-                            device.wakeUpAndGoToHomeScreen()
-                        }
-                        eachRun {
-                            device.pressHome()
-                            testApp.launchViaIntent(wmHelper)
-                            this.setRotation(configuration.startRotation)
-                        }
-                    }
-                    teardown {
-                        eachRun {
-                            if (device.hasPipWindow()) {
-                                device.closePipWindow()
-                            }
-                            testApp.exit()
-                            this.setRotation(Surface.ROTATION_0)
-                        }
-                        test {
-                            if (device.hasPipWindow()) {
-                                device.closePipWindow()
-                            }
-                        }
-                    }
-                    transitions {
-                        testApp.clickEnterPipButton()
-                        device.expandPipWindow()
-                    }
-                    assertions {
+            val baseConfig = getTransitionLaunch(
+                eachRun = true, stringExtras = emptyMap())
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                transitions {
+                    pipApp.clickEnterPipButton()
+                    pipApp.expandPipWindow(wmHelper)
+                }
+                assertions {
+                    presubmit {
                         windowManagerTrace {
                             navBarWindowIsAlwaysVisible()
                             statusBarWindowIsAlwaysVisible()
 
                             all("pipWindowBecomesVisible") {
-                                this.showsAppWindow(testApp.`package`)
-                                    .then()
-                                    .showsAppWindow(PIP_WINDOW_TITLE)
+                                this.showsAppWindow(pipApp.defaultWindowName)
                             }
                         }
 
                         layersTrace {
-                            navBarLayerIsAlwaysVisible(bugId = 140855415)
                             statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
-                                enabled = false)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0, bugId = 140855415)
                             statusBarLayerRotatesScales(configuration.startRotation,
                                 Surface.ROTATION_0)
                         }
 
                         layersTrace {
                             all("pipLayerBecomesVisible") {
-                                this.showsLayer(testApp.launcherName)
-                                    .then()
-                                    .showsLayer(PIP_WINDOW_TITLE)
+                                this.showsLayer(pipApp.launcherName)
                             }
                         }
                     }
+
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                        }
+                    }
                 }
+            }
+
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+                testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+                repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
new file mode 100644
index 0000000..eaaa2f6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
+import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterPipToOtherOrientationTest(
+    testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+        private val testApp = FixedAppHelper(instrumentation)
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+                supportedRotations = listOf(Surface.ROTATION_0),
+                repetitions = 5) { configuration ->
+                setupAndTeardown(this, configuration)
+
+                setup {
+                    eachRun {
+                        // Launch a portrait only app on the fullscreen stack
+                        testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+                            EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
+                        // Launch the PiP activity fixed as landscape
+                        pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+                            EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
+                    }
+                }
+                teardown {
+                    eachRun {
+                        pipApp.exit()
+                        testApp.exit()
+                    }
+                }
+                transitions {
+                    // Enter PiP, and assert that the PiP is within bounds now that the device is back
+                    // in portrait
+                    broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
+                    wmHelper.waitPipWindowShown()
+                    wmHelper.waitForAppTransitionIdle()
+                }
+                assertions {
+                    val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+                    val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+
+                    presubmit {
+                        windowManagerTrace {
+                            all("pipApp window is always on top") {
+                                showsAppWindowOnTop(pipApp.defaultWindowName)
+                            }
+                            start("pipApp window hides testApp") {
+                                isInvisible(testApp.defaultWindowName)
+                            }
+                            end("testApp windows is shown") {
+                                isVisible(testApp.defaultWindowName)
+                            }
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+
+                        layersTrace {
+                            start("pipApp layer hides testApp") {
+                                hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
+                                isInvisible(testApp.defaultWindowName)
+                            }
+                        }
+                    }
+
+                    flaky {
+                        layersTrace {
+                            end("testApp layer covers fullscreen") {
+                                hasVisibleRegion(testApp.defaultWindowName, endingBounds)
+                            }
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
new file mode 100644
index 0000000..707d28d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
+import com.android.server.wm.traces.common.windowmanager.WindowManagerState
+import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.google.common.truth.Truth
+
+inline val WindowManagerState.pinnedWindows
+    get() = visibleWindows
+        .filter { it.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED }
+
+/**
+ * Checks if the state has any window in PIP mode
+ */
+fun WindowManagerState.hasPipWindow(): Boolean = pinnedWindows.isNotEmpty()
+
+/**
+ * Checks that an activity [activity] is in PIP mode
+ */
+fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
+    val windowName = activity.toWindowName()
+    return pinnedWindows.any { it.title == windowName }
+}
+
+/**
+ * Asserts that an activity [activity] exists and is in PIP mode
+ */
+fun WindowManagerStateSubject.isInPipMode(
+    activity: ComponentName
+): WindowManagerStateSubject = apply {
+    val windowName = activity.toWindowName()
+    hasWindow(windowName)
+    val pinnedWindows = wmState.pinnedWindows
+        .map { it.title }
+    Truth.assertWithMessage("Window not in PIP mode")
+        .that(pinnedWindows)
+        .contains(windowName)
+}
+
+/**
+ * Waits until the state has a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowShown(): Boolean =
+    waitFor("PIP window shown") {
+        val result = it.wmState.hasPipWindow()
+        result
+    }
+
+/**
+ * Waits until the state doesn't have a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowGone(): Boolean =
+    waitFor("PIP window gone") {
+        val result = !it.wmState.hasPipWindow()
+        result
+    }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index c21b594..7576e24 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,21 +16,19 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.startRotation
 import com.android.wm.shell.flicker.IME_WINDOW_NAME
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.testapp.Components
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,114 +37,61 @@
  * Test Pip launch.
  * To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipKeyboardTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    private val keyboardApp = ImeAppHelper(instrumentation)
-    private val keyboardComponent = Components.ImeActivity.COMPONENT
-    private val helper = WindowManagerStateHelper()
-
-    private val keyboardScenario: FlickerBuilder
-        get() = FlickerBuilder(instrumentation).apply {
-            repeat { TEST_REPETITIONS }
-            // disable layer tracing
-            withLayerTracing { null }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    device.pressHome()
-                    // launch our target pip app
-                    testApp.launchViaIntent(wmHelper)
-                    this.setRotation(rotation)
-                    testApp.clickEnterPipButton()
-                    // open an app with an input field and a keyboard
-                    // UiAutomator doesn't support to launch the multiple Activities in a task.
-                    // So use launchActivity() for the Keyboard Activity.
-                    keyboardApp.launchViaIntent()
-                    helper.waitForAppTransitionIdle()
-                    helper.waitForFullScreenApp(keyboardComponent)
-                }
-            }
-            teardown {
-                test {
-                    keyboardApp.exit()
-
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-        }
-
-    /** Ensure the pip window remains visible throughout any keyboard interactions. */
-    @Test
-    fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() {
-        val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose"
-        runWithFlicker(keyboardScenario) {
-            withTestName { testTag }
-            transitions {
-                // open the soft keyboard
-                keyboardApp.openIME(device, wmHelper)
-                helper.waitImeWindowShown()
-
-                // then close it again
-                keyboardApp.closeIME(device, wmHelper)
-                helper.waitImeWindowGone()
-            }
-            assertions {
-                windowManagerTrace {
-                    all("PiP window must remain inside visible bounds") {
-                        val displayBounds = WindowUtils.getDisplayBounds(rotation)
-                        coversAtMostRegion(testApp.defaultWindowName, displayBounds)
-                    }
-                }
-            }
-        }
-    }
-
-    /** Ensure the pip window does not obscure the keyboard. */
-    @Test
-    fun pipWindow_doesNotObscure_keyboard() {
-        val testTag = "pipWindow_doesNotObscure_keyboard"
-        runWithFlicker(keyboardScenario) {
-            withTestName { testTag }
-            transitions {
-                // open the soft keyboard
-                keyboardApp.openIME(device, wmHelper)
-                helper.waitImeWindowShown()
-            }
-            teardown {
-                eachRun {
-                    // close the keyboard
-                    keyboardApp.closeIME(device, wmHelper)
-                    helper.waitImeWindowGone()
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    end("imeWindowAboveApp") {
-                        isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName)
-                    }
-                }
-            }
-        }
-    }
-
-    companion object {
-        private const val TEST_REPETITIONS = 5
+class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+        private const val TAG_IME_VISIBLE = "imeIsVisible"
 
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+            val imeApp = ImeAppHelper(instrumentation)
+            val baseConfig = getTransitionLaunch(eachRun = false)
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                setup {
+                    test {
+                        imeApp.launchViaIntent(wmHelper)
+                        setRotation(configuration.startRotation)
+                    }
+                }
+                teardown {
+                    test {
+                        imeApp.exit()
+                        setRotation(Surface.ROTATION_0)
+                    }
+                }
+                transitions {
+                    // open the soft keyboard
+                    imeApp.openIME(wmHelper)
+                    createTag(TAG_IME_VISIBLE)
+
+                    // then close it again
+                    imeApp.closeIME(wmHelper)
+                }
+                assertions {
+                    presubmit {
+                        windowManagerTrace {
+                            // Ensure the pip window remains visible throughout
+                            // any keyboard interactions
+                            all("pipInVisibleBounds") {
+                                val displayBounds = WindowUtils.getDisplayBounds(
+                                    configuration.startRotation)
+                                coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+                            }
+                            // Ensure that the pip window does not obscure the keyboard
+                            tag(TAG_IME_VISIBLE) {
+                                isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+                            }
+                        }
+                    }
+                }
+            }
+
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+                baseConfig, testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+                repetitions = 5)
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index e579096..f10bd7f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -32,6 +32,7 @@
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.removeAllTasksButHome
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.FixMethodOrder
 import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
deleted file mode 100644
index 5e0760c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.content.Intent
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assert.assertEquals
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipOrientationTest(
-    rotationName: String,
-    rotation: Int
-) : AppTestBase(rotationName, rotation) {
-    // Helper class to process test actions by broadcast.
-    private inner class BroadcastActionTrigger {
-        private fun createIntentWithAction(broadcastAction: String): Intent {
-            return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-        }
-        fun doAction(broadcastAction: String) {
-            instrumentation.getContext().sendBroadcast(createIntentWithAction(broadcastAction))
-        }
-        fun requestOrientationForPip(orientation: Int) {
-            instrumentation.getContext()
-                    .sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
-                    .putExtra(EXTRA_PIP_ORIENTATION, orientation.toString()))
-        }
-    }
-    private val broadcastActionTrigger = BroadcastActionTrigger()
-
-    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
-    private val ORIENTATION_LANDSCAPE = 0
-    // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
-    private val ORIENTATION_PORTRAIT = 1
-
-    private val testApp = FixedAppHelper(instrumentation)
-    private val pipApp = PipAppHelper(instrumentation)
-
-    @Test
-    fun testEnterPipToOtherOrientation() {
-        runFlicker(instrumentation) {
-            withTestName { "testEnterPipToOtherOrientation" }
-            setup {
-                test {
-                    removeAllTasksButHome()
-                    device.wakeUpAndGoToHomeScreen()
-                    // Launch a portrait only app on the fullscreen stack
-                    testApp.launchViaIntent(stringExtras = mapOf(
-                            EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
-                    waitForAnimationComplete()
-                    // Launch the PiP activity fixed as landscape
-                    pipApp.launchViaIntent(stringExtras = mapOf(
-                            EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
-                    waitForAnimationComplete()
-                }
-            }
-            transitions {
-                // Enter PiP, and assert that the PiP is within bounds now that the device is back
-                // in portrait
-                broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
-                waitForAnimationComplete()
-            }
-            teardown {
-                test {
-                    removeAllTasksButHome()
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    all("pipApp window is always on top") {
-                        showsAppWindowOnTop(pipApp.defaultWindowName)
-                    }
-                    start("pipApp window hides testApp") {
-                        isInvisible(testApp.defaultWindowName)
-                    }
-                    end("testApp windows is shown") {
-                        isVisible(testApp.defaultWindowName)
-                    }
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-                layersTrace {
-                    val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
-                    val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
-                    start("pipApp layer hides testApp") {
-                        hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
-                        isInvisible(testApp.defaultWindowName)
-                    }
-                    end("testApp layer covers fullscreen", enabled = false) {
-                        hasVisibleRegion(testApp.defaultWindowName, endingBounds)
-                    }
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                }
-            }
-        }
-    }
-
-    @Test
-    fun testSetRequestedOrientationWhilePinned() {
-        runFlicker(instrumentation) {
-            withTestName { "testSetRequestedOrientationWhilePinned" }
-            setup {
-                test {
-                    removeAllTasksButHome()
-                    device.wakeUpAndGoToHomeScreen()
-                    // Launch the PiP activity fixed as landscape
-                    pipApp.launchViaIntent(stringExtras = mapOf(
-                            EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
-                            EXTRA_ENTER_PIP to "true"))
-                    waitForAnimationComplete()
-                    assertEquals(Surface.ROTATION_0, device.displayRotation)
-                }
-            }
-            transitions {
-                // Request that the orientation is set to landscape
-                broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
-
-                // Launch the activity back into fullscreen and ensure that it is now in landscape
-                pipApp.launchViaIntent()
-                waitForAnimationComplete()
-                assertEquals(Surface.ROTATION_90, device.displayRotation)
-            }
-            teardown {
-                test {
-                    removeAllTasksButHome()
-                }
-            }
-            assertions {
-                val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
-                val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
-                windowManagerTrace {
-                    start("PIP window must remain inside display") {
-                        coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
-                    }
-                    end("pipApp shows on top") {
-                        showsAppWindowOnTop(pipApp.defaultWindowName)
-                    }
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-                layersTrace {
-                    start("PIP layer must remain inside display") {
-                        coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
-                    }
-                    end("pipApp layer covers fullscreen") {
-                        hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
-                    }
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                }
-            }
-        }
-    }
-
-    companion object {
-        @Parameterized.Parameters(name = "{0}")
-        @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
-        }
-    }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index a00c5f4..adab5e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -16,21 +16,18 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Presubmit
+import android.os.Bundle
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
 import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -38,7 +35,6 @@
 import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.FixMethodOrder
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -48,80 +44,77 @@
  * Test Pip Stack in bounds after rotations.
  * To run this test: `atest WMShellFlickerTests:PipRotationTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class PipRotationTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
-    companion object {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val instrumentation = InstrumentationRegistry.getInstrumentation()
-            val testApp = FixedAppHelper(instrumentation)
-            val pipApp = PipAppHelper(instrumentation)
-            return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
-                supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) {
-                configuration ->
-                        withTestName { buildTestTag("PipRotationTest", testApp, configuration) }
-                        repeat { configuration.repetitions }
-                        setup {
-                            test {
-                                AppTestBase.removeAllTasksButHome()
-                                device.wakeUpAndGoToHomeScreen()
-                                pipApp.launchViaIntent(stringExtras = mapOf(
-                                    EXTRA_ENTER_PIP to "true"))
-                                testApp.launchViaIntent()
-                                AppTestBase.waitForAnimationComplete()
-                            }
-                            eachRun {
-                                setRotation(configuration.startRotation)
-                            }
+            val fixedApp = FixedAppHelper(instrumentation)
+            val baseConfig = getTransitionLaunch(eachRun = false)
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                setup {
+                    test {
+                        fixedApp.launchViaIntent(wmHelper)
+                    }
+                    eachRun {
+                        setRotation(configuration.startRotation)
+                    }
+                }
+                transitions {
+                    setRotation(configuration.endRotation)
+                }
+                teardown {
+                    eachRun {
+                        setRotation(Surface.ROTATION_0)
+                    }
+                }
+                assertions {
+                    val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+                    val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
+
+                    presubmit {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
                         }
-                        transitions {
-                            setRotation(configuration.endRotation)
+
+                        layersTrace {
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false)
                         }
-                        teardown {
-                            eachRun {
-                                setRotation(Surface.ROTATION_0)
+                    }
+
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+
+                            start("appLayerRotates_StartingBounds", bugId = 140855415) {
+                                hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
+                                coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
                             }
-                            test {
-                                AppTestBase.removeAllTasksButHome()
-                            }
-                        }
-                        assertions {
-                            windowManagerTrace {
-                                navBarWindowIsAlwaysVisible()
-                                statusBarWindowIsAlwaysVisible()
-                            }
-                            layersTrace {
-                                navBarLayerIsAlwaysVisible(bugId = 140855415)
-                                statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                                noUncoveredRegions(configuration.startRotation,
-                                    configuration.endRotation, allStates = false)
-                                navBarLayerRotatesAndScales(configuration.startRotation,
-                                    configuration.endRotation, bugId = 140855415)
-                                statusBarLayerRotatesScales(configuration.startRotation,
-                                    configuration.endRotation, bugId = 140855415)
-                            }
-                            layersTrace {
-                                val startingBounds = WindowUtils.getDisplayBounds(
-                                    configuration.startRotation)
-                                val endingBounds = WindowUtils.getDisplayBounds(
-                                    configuration.endRotation)
-                                start("appLayerRotates_StartingBounds", bugId = 140855415) {
-                                    hasVisibleRegion(testApp.defaultWindowName, startingBounds)
-                                    coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
-                                }
-                                end("appLayerRotates_EndingBounds", bugId = 140855415) {
-                                    hasVisibleRegion(testApp.defaultWindowName, endingBounds)
-                                    coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
-                                }
+                            end("appLayerRotates_EndingBounds", bugId = 140855415) {
+                                hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
+                                coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
                             }
                         }
                     }
+                }
+            }
+
+            return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
+                baseConfig, testSpec,
+                supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+                repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 3e7eb134..4b826ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -16,29 +16,23 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.os.Bundle
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
 import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.helpers.PipAppHelper
 import org.junit.FixMethodOrder
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -51,48 +45,30 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
 class PipToAppTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
-    companion object {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val instrumentation = InstrumentationRegistry.getInstrumentation()
-            val testApp = PipAppHelper(instrumentation)
-            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
-                supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
-                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
-                    repeat { configuration.repetitions }
-                    setup {
-                        test {
-                            device.wakeUpAndGoToHomeScreen()
-                            device.pressHome()
-                            testApp.launchViaIntent(wmHelper)
-                        }
-                        eachRun {
-                            this.setRotation(configuration.startRotation)
-                            testApp.clickEnterPipButton()
-                            device.hasPipWindow()
-                        }
+            val baseConfig = getTransitionLaunch(eachRun = true)
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                setup {
+                    eachRun {
+                        this.setRotation(configuration.startRotation)
                     }
-                    teardown {
-                        eachRun {
-                            this.setRotation(Surface.ROTATION_0)
-                        }
-                        test {
-                            if (device.hasPipWindow()) {
-                                device.closePipWindow()
-                            }
-                            testApp.exit()
-                        }
+                }
+                teardown {
+                    eachRun {
+                        this.setRotation(Surface.ROTATION_0)
                     }
-                    transitions {
-                        device.expandPipWindow()
-                        device.waitForIdle()
-                    }
-                    assertions {
+                }
+                transitions {
+                    pipApp.expandPipWindowToApp(wmHelper)
+                }
+                assertions {
+                    presubmit {
                         windowManagerTrace {
                             navBarWindowIsAlwaysVisible()
                             statusBarWindowIsAlwaysVisible()
@@ -100,34 +76,42 @@
                             all("appReplacesPipWindow") {
                                 this.showsAppWindow(PIP_WINDOW_TITLE)
                                     .then()
-                                    .showsAppWindowOnTop(testApp.launcherName)
+                                    .showsAppWindowOnTop(pipApp.launcherName)
                             }
                         }
 
                         layersTrace {
-                            navBarLayerIsAlwaysVisible(bugId = 140855415)
                             statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
-                                enabled = false)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0, bugId = 140855415)
                             statusBarLayerRotatesScales(configuration.startRotation,
                                 Surface.ROTATION_0)
 
                             all("appReplacesPipLayer") {
                                 this.showsLayer(PIP_WINDOW_TITLE)
                                     .then()
-                                    .showsLayer(testApp.launcherName)
+                                    .showsLayer(pipApp.launcherName)
                             }
                         }
+                    }
+
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                        }
 
                         eventLog {
                             focusChanges(
-                                "NexusLauncherActivity", testApp.launcherName,
+                                "NexusLauncherActivity", pipApp.launcherName,
                                 "NexusLauncherActivity", bugId = 151179149)
                         }
                     }
                 }
+            }
+
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+                testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
index 5d3bc13..62e8221 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
@@ -16,28 +16,23 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.os.Bundle
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
 import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.helpers.PipAppHelper
 import org.junit.FixMethodOrder
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -50,50 +45,30 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
 class PipToHomeTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
-    companion object {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val instrumentation = InstrumentationRegistry.getInstrumentation()
-            val testApp = PipAppHelper(instrumentation)
-            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
-                supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
-                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
-                    repeat { configuration.repetitions }
-                    setup {
-                        test {
-                            device.wakeUpAndGoToHomeScreen()
-                            device.pressHome()
-                        }
-                        eachRun {
-                            testApp.launchViaIntent(wmHelper)
-                            this.setRotation(configuration.startRotation)
-                            testApp.clickEnterPipButton()
-                            device.hasPipWindow()
-                        }
+            val baseConfig = getTransitionLaunch(eachRun = true)
+            val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+                setup {
+                    eachRun {
+                        this.setRotation(configuration.startRotation)
                     }
-                    teardown {
-                        eachRun {
-                            this.setRotation(Surface.ROTATION_0)
-                            if (device.hasPipWindow()) {
-                                device.closePipWindow()
-                            }
-                        }
-                        test {
-                            if (device.hasPipWindow()) {
-                                device.closePipWindow()
-                            }
-                            testApp.exit()
-                        }
+                }
+                teardown {
+                    eachRun {
+                        this.setRotation(Surface.ROTATION_0)
                     }
-                    transitions {
-                        testApp.closePipWindow()
-                    }
-                    assertions {
+                }
+                transitions {
+                    pipApp.closePipWindow(wmHelper)
+                }
+                assertions {
+                    presubmit {
                         windowManagerTrace {
                             navBarWindowIsAlwaysVisible()
                             statusBarWindowIsAlwaysVisible()
@@ -106,12 +81,7 @@
                         }
 
                         layersTrace {
-                            navBarLayerIsAlwaysVisible(bugId = 140855415)
                             statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
-                                enabled = false)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0, bugId = 140855415)
                             statusBarLayerRotatesScales(configuration.startRotation,
                                 Surface.ROTATION_0)
 
@@ -121,13 +91,28 @@
                                     .hidesLayer(PIP_WINDOW_TITLE)
                             }
                         }
+                    }
 
+                    postsubmit {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                        }
+                    }
+
+                    flaky {
                         eventLog {
-                            focusChanges(testApp.launcherName, "NexusLauncherActivity",
+                            focusChanges(pipApp.launcherName, "NexusLauncherActivity",
                                 bugId = 151179149)
                         }
                     }
                 }
+            }
+
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, baseConfig,
+                testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
new file mode 100644
index 0000000..eb7bae1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.os.Bundle
+import android.view.Surface
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.repetitions
+import com.android.wm.shell.flicker.helpers.PipAppHelper
+import com.android.wm.shell.flicker.removeAllTasksButHome
+import com.android.wm.shell.flicker.testapp.Components
+
+abstract class PipTransitionBase(protected val instrumentation: Instrumentation) {
+    // Helper class to process test actions by broadcast.
+    protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
+        private fun createIntentWithAction(broadcastAction: String): Intent {
+            return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+        }
+
+        fun doAction(broadcastAction: String) {
+            instrumentation.context
+                .sendBroadcast(createIntentWithAction(broadcastAction))
+        }
+
+        fun requestOrientationForPip(orientation: Int) {
+            instrumentation.context.sendBroadcast(
+                    createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION)
+                    .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString())
+            )
+        }
+
+        companion object {
+            // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+            @JvmStatic
+            val ORIENTATION_LANDSCAPE = 0
+
+            // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+            @JvmStatic
+            val ORIENTATION_PORTRAIT = 1
+        }
+    }
+
+    protected val pipApp = PipAppHelper(instrumentation)
+    protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+
+    /**
+     * Gets a configuration that handles basic setup and teardown of pip tests
+     */
+    protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit
+        get() = { configuration ->
+            withTestName { buildTestTag(configuration) }
+            repeat { configuration.repetitions }
+            setup {
+                test {
+                    removeAllTasksButHome()
+                    device.wakeUpAndGoToHomeScreen()
+                }
+            }
+            teardown {
+                eachRun {
+                    setRotation(Surface.ROTATION_0)
+                }
+                test {
+                    removeAllTasksButHome()
+
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                    pipApp.exit()
+                }
+            }
+        }
+
+    /**
+     * Gets a configuration that handles basic setup and teardown of pip tests and that
+     * launches the Pip app for test
+     *
+     * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
+     * @param stringExtras Arguments to pass to the PIP launch intent
+     */
+    @JvmOverloads
+    fun getTransitionLaunch(
+        eachRun: Boolean,
+        stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true")
+    ): FlickerBuilder.(Bundle) -> Unit {
+        return { configuration ->
+            setupAndTeardown(this, configuration)
+
+            setup {
+                test {
+                    removeAllTasksButHome()
+                    if (!eachRun) {
+                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        wmHelper.waitPipWindowShown()
+                    }
+                }
+                eachRun {
+                    if (eachRun) {
+                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        wmHelper.waitPipWindowShown()
+                    }
+                }
+            }
+            teardown {
+                eachRun {
+                    if (eachRun) {
+                        pipApp.exit()
+                    }
+                }
+                test {
+                    if (!eachRun) {
+                        pipApp.exit()
+                    }
+                    removeAllTasksButHome()
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
new file mode 100644
index 0000000..c01bc94
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
+import org.junit.Assert.assertEquals
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SetRequestedOrientationWhilePinnedTest(
+    testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+    companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+                supportedRotations = listOf(Surface.ROTATION_0),
+                repetitions = 1) { configuration ->
+                setupAndTeardown(this, configuration)
+
+                setup {
+                    eachRun {
+                        // Launch the PiP activity fixed as landscape
+                        pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+                            EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
+                            EXTRA_ENTER_PIP to "true"))
+                    }
+                }
+                teardown {
+                    eachRun {
+                        pipApp.exit()
+                    }
+                }
+                transitions {
+                    // Request that the orientation is set to landscape
+                    broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
+
+                    // Launch the activity back into fullscreen and
+                    // ensure that it is now in landscape
+                    pipApp.launchViaIntent(wmHelper)
+                    wmHelper.waitForFullScreenApp(pipApp.component)
+                    wmHelper.waitForRotation(Surface.ROTATION_90)
+                    assertEquals(Surface.ROTATION_90, device.displayRotation)
+                }
+                assertions {
+                    val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+                    val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+                    presubmit {
+                        windowManagerTrace {
+                            start("PIP window must remain inside display") {
+                                coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
+                            }
+                            end("pipApp shows on top") {
+                                showsAppWindowOnTop(pipApp.defaultWindowName)
+                            }
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+                        layersTrace {
+                            start("PIP layer must remain inside display") {
+                                coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+                            }
+                            end("pipApp layer covers fullscreen") {
+                                hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
+                            }
+                        }
+                    }
+
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
 android_test {
     name: "WMShellUnitTests",
 
-    srcs: ["**/*.java"],
+    srcs: [
+        "**/*.java",
+        "**/*.kt",
+    ],
 
     static_libs: [
         "WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringForce
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.animation.PhysicsAnimator.EndListener
 import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
 import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
     private lateinit var viewGroup: ViewGroup
     private lateinit var testView: View
     private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
 import com.android.wm.shell.ShellTestCase
 import junit.framework.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
 import android.view.View
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
     /** Incrementing value for fake MotionEvent timestamps. */
     private var time = 0L
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 2572106..19ecc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
@@ -65,7 +66,7 @@
 
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,7 +93,7 @@
 
     // Both the split-screen and start interface.
     @Mock
-    private SplitScreen mSplitScreenStarter;
+    private SplitScreenController mSplitScreenStarter;
 
     private DisplayLayout mLandscapeDisplayLayout;
     private DisplayLayout mPortraitDisplayLayout;
@@ -127,8 +128,8 @@
         mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
         mInsets = Insets.of(0, 0, 0, 0);
 
-        mPolicy = new DragAndDropPolicy(
-                mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
+        mPolicy = spy(new DragAndDropPolicy(
+                mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter));
         mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -204,8 +205,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -216,13 +217,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -233,13 +234,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -250,8 +251,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -262,8 +263,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -275,14 +276,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -294,14 +295,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 0087d91..3147dab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.wm.shell.pip;
 
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 
@@ -34,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -84,7 +88,7 @@
     public void getAnimator_withBounds_returnBoundsAnimator() {
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         assertEquals("Expect ANIM_TYPE_BOUNDS animation",
                 animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -98,13 +102,13 @@
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
         oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
         oldAnimator.start();
 
         final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         assertEquals("getAnimator with same type returns same animator",
                 oldAnimator, newAnimator);
@@ -128,6 +132,21 @@
     }
 
     @Test
+    public void pipTransitionAnimator_rotatedEndValue() {
+        final Rect startBounds = new Rect(200, 700, 400, 800);
+        final Rect endBounds = new Rect(0, 0, 500, 1000);
+        final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+                .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90);
+        // Apply fraction 1 to compute the end value.
+        animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
+        final Rect rotatedEndBounds = new Rect(endBounds);
+        DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
+
+        assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
+    }
+
+    @Test
     @SuppressWarnings("unchecked")
     public void pipTransitionAnimator_updateEndValue() {
         final Rect baseValue = new Rect(0, 0, 100, 100);
@@ -136,7 +155,7 @@
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         animator.updateEndValue(endValue2);
 
@@ -150,7 +169,7 @@
         final Rect endValue = new Rect(100, 100, 200, 200);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
         animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
 
         animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 414a0a7..27e5f51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -47,6 +47,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
 
 import org.junit.Test;
@@ -83,7 +84,7 @@
                 createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
                 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
                 taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(),
-                null /* clearWindow */);
+                null /* clearWindow */, new TestShellExecutor());
     }
 
     private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ce1d96c..f481228 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -335,7 +335,6 @@
         "jni/YuvToJpegEncoder.cpp",
         "jni/fonts/Font.cpp",
         "jni/fonts/FontFamily.cpp",
-        "jni/fonts/NativeFont.cpp",
         "jni/text/LineBreaker.cpp",
         "jni/text/MeasuredText.cpp",
         "jni/text/TextShaper.cpp",
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 27be622..d5fee3f 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -35,7 +35,6 @@
 
 public:
     static DeviceInfo* get();
-    static float getMaxRefreshRate() { return get()->mMaxRefreshRate; }
     static int32_t getWidth() { return get()->mWidth; }
     static int32_t getHeight() { return get()->mHeight; }
     // Gets the density in density-independent pixels
@@ -45,7 +44,6 @@
     static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; }
     // Sets the density in density-independent pixels
     static void setDensity(float density) { sDensity.store(density); }
-    static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; }
     static void setWidth(int32_t width) { get()->mWidth = width; }
     static void setHeight(int32_t height) { get()->mHeight = height; }
     static void setRefreshRate(float refreshRate) {
@@ -91,7 +89,6 @@
     SkColorType mWideColorType = SkColorType::kN32_SkColorType;
     int mDisplaysSize = 0;
     int mPhysicalDisplayIndex = -1;
-    float mMaxRefreshRate = 60.0;
     int32_t mWidth = 1080;
     int32_t mHeight = 1920;
     int64_t mVsyncPeriod = 16666666;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index ca2ada9..b14ade9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -54,7 +54,7 @@
 }
 
 static inline SkScalar isIntegerAligned(SkScalar x) {
-    return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+    return MathUtils::isZero(roundf(x) - x);
 }
 
 // Disable filtering when there is no scaling in screen coordinates and the corners have the same
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e798f2a..971a53a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -79,7 +79,6 @@
 bool Properties::isolatedProcess = false;
 
 int Properties::contextPriority = 0;
-int Properties::defaultRenderAhead = -1;
 float Properties::defaultSdrWhitePoint = 200.f;
 
 bool Properties::load() {
@@ -129,10 +128,6 @@
 
     runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
 
-    defaultRenderAhead = std::max(
-            -1,
-            std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
-
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1639143..dcb79ba 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,8 +162,6 @@
  */
 #define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
 
-#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
-
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -247,8 +245,6 @@
 
     static int contextPriority;
 
-    static int defaultRenderAhead;
-
     static float defaultSdrWhitePoint;
 
 private:
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 0fad2d5..e1f5abd7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -69,7 +69,6 @@
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_fonts_Font(JNIEnv* env);
 extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
-extern int register_android_graphics_fonts_NativeFont(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -136,7 +135,6 @@
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
     REG_JNI(register_android_graphics_fonts_Font),
     REG_JNI(register_android_graphics_fonts_FontFamily),
-    REG_JNI(register_android_graphics_fonts_NativeFont),
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
     REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 97c40d6..fa1752c 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -115,6 +115,18 @@
     return reinterpret_cast<jlong>(composeFilter.release());
 }
 
+static jlong createShaderEffect(
+    JNIEnv* env,
+    jobject,
+    jlong shaderHandle
+) {
+    auto* shader = reinterpret_cast<const SkShader*>(shaderHandle);
+    sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader(
+          sk_ref_sp(shader), nullptr
+    );
+    return reinterpret_cast<jlong>(shaderFilter.release());
+}
+
 static void RenderEffect_safeUnref(SkImageFilter* filter) {
     SkSafeUnref(filter);
 }
@@ -130,7 +142,8 @@
     {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
     {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
     {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
-    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}
+    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+    {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
 };
 
 int register_android_graphics_RenderEffect(JNIEnv* env) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index a146b64..4966bfa 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -603,14 +603,12 @@
 
 static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
                                                           jint physicalHeight, jfloat refreshRate,
-                                                          jfloat maxRefreshRate,
                                                           jint wideColorDataspace,
                                                           jlong appVsyncOffsetNanos,
                                                           jlong presentationDeadlineNanos) {
     DeviceInfo::setWidth(physicalWidth);
     DeviceInfo::setHeight(physicalHeight);
     DeviceInfo::setRefreshRate(refreshRate);
-    DeviceInfo::setMaxRefreshRate(maxRefreshRate);
     DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
     DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
     DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
@@ -735,7 +733,7 @@
         {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
         {"nSetDisplayDensityDpi", "(I)V",
          (void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
-        {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
+        {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
         {"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
 };
 
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b769d40..c8471a9 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -34,6 +34,7 @@
 #include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
 #include <minikin/FontFileParser.h>
+#include <minikin/LocaleList.h>
 #include <ui/FatVector.h>
 
 #include <memory>
@@ -149,12 +150,8 @@
     return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
 }
 
-// Critical Native
-static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
-    return reinterpret_cast<jlong>(releaseFont);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
+// Font JNI functions
 
 // Fast Native
 static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
@@ -195,51 +192,92 @@
 }
 
 // Critical Native
-static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
-    return reinterpret_cast<jlong>(font->font.get());
+static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    return reinterpret_cast<jlong>(font->font->typeface().get());
 }
 
 // Critical Native
-static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
-    const void* bufferPtr = font->font->typeface()->GetFontData();
-    return reinterpret_cast<jlong>(bufferPtr);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct FontBufferWrapper {
-    FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
-    // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
-    std::shared_ptr<minikin::MinikinFont> minikinFont;
-};
-
-static void unrefBuffer(jlong nativePtr) {
-    FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
-    delete wrapper;
-}
-
-// Critical Native
-static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    return reinterpret_cast<jlong>(new FontBufferWrapper(font->typeface()));
+static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    std::shared_ptr<minikin::Font> ref = font->font;
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(ref)));
 }
 
 // Fast Native
-static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
-    FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
-    return env->NewDirectByteBuffer(
-        const_cast<void*>(wrapper->minikinFont->GetFontData()),
-        wrapper->minikinFont->GetFontSize());
+static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()),
+                                    minikinFont->GetFontSize());
 }
 
 // Critical Native
-static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
-    return reinterpret_cast<jlong>(unrefBuffer);
+static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    return reinterpret_cast<jlong>(font->font->typeface()->GetFontData());
 }
 
-///////////////////////////////////////////////////////////////////////////////
+// Critical Native
+static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
+    return reinterpret_cast<jlong>(releaseFont);
+}
+
+// Fast Native
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    const std::string& path = minikinFont->GetFontPath();
+    if (path.empty()) {
+        return nullptr;
+    }
+    return env->NewStringUTF(path.c_str());
+}
+
+// Fast Native
+static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    uint32_t localeListId = font->font->getLocaleListId();
+    if (localeListId == 0) {
+        return nullptr;
+    }
+    std::string langTags = minikin::getLocaleString(localeListId);
+    if (langTags.empty()) {
+        return nullptr;
+    }
+    return env->NewStringUTF(langTags.c_str());
+}
+
+// Critical Native
+static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    uint32_t weight = font->font->style().weight();
+    uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0;
+    return (isItalic << 16) | weight;
+}
+
+// Critical Native
+static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return minikinFont->GetFontIndex();
+}
+
+// Critical Native
+static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return minikinFont->GetAxes().size();
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    minikin::FontVariation var = minikinFont->GetAxes().at(index);
+    uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+    return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
 
 // Fast Native
 static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
@@ -314,20 +352,23 @@
         {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
          (void*)Font_Builder_build},
         {"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
-        {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
 };
 
 static const JNINativeMethod gFontMethods[] = {
-    { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
-    { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
-    { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
-    { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress },
-};
-
-static const JNINativeMethod gFontBufferHelperMethods[] = {
-    { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
-    { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
-    { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
+        {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr},
+        {"nCloneFont", "(J)J", (void*)Font_cloneFont},
+        {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer},
+        {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress},
+        {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc},
+        {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds},
+        {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+         (void*)Font_getFontMetrics},
+        {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath},
+        {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList},
+        {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle},
+        {"nGetIndex", "(J)I", (void*)Font_getIndex},
+        {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount},
+        {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo},
 };
 
 static const JNINativeMethod gFontFileUtilMethods[] = {
@@ -343,8 +384,6 @@
             NELEM(gFontBuilderMethods)) +
             RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
             NELEM(gFontMethods)) +
-            RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
-            gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) +
             RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods,
             NELEM(gFontFileUtilMethods));
 }
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index a07723f..b682135 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -95,22 +95,36 @@
 }
 
 // CriticalNative
-static jint FontFamily_getVariant(jlong familyPtr) {
+static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
     FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
     return static_cast<jint>(family->family->variant());
 }
 
+// CriticalNative
+static jint FontFamily_getFontSize(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    return family->family->getNumFonts();
+}
+
+// CriticalNative
+static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint index) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    std::shared_ptr<minikin::Font> font = family->family->getFontRef(index);
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontFamilyBuilderMethods[] = {
     { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
     { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
     { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
-
     { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
 };
 
 static const JNINativeMethod gFontFamilyMethods[] = {
+        {"nGetFontSize", "(J)I", (void*)FontFamily_getFontSize},
+        {"nGetFont", "(JI)J", (void*)FontFamily_getFont},
         {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
         {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
 };
diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp
deleted file mode 100644
index c5c5d46..0000000
--- a/libs/hwui/jni/fonts/NativeFont.cpp
+++ /dev/null
@@ -1,125 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
-#include "Font.h"
-#include "SkData.h"
-#include "SkFont.h"
-#include "SkFontMetrics.h"
-#include "SkFontMgr.h"
-#include "SkRefCnt.h"
-#include "SkTypeface.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/ScopedUtfChars.h>
-#include "Utils.h"
-#include "FontUtils.h"
-
-#include <hwui/MinikinSkia.h>
-#include <hwui/Paint.h>
-#include <hwui/Typeface.h>
-#include <minikin/FontFamily.h>
-#include <minikin/LocaleList.h>
-#include <ui/FatVector.h>
-
-#include <memory>
-
-namespace android {
-
-// Critical Native
-static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) {
-    Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
-    return tf->fFontCollection->getFamilies().size();
-}
-
-// Critical Native
-static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) {
-    Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
-    return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get());
-
-}
-
-// Fast Native
-static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    uint32_t localeListId = family->localeListId();
-    return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str());
-}
-
-// Critical Native
-static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    return family->getNumFonts();
-}
-
-// Critical Native
-static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    return reinterpret_cast<jlong>(family->getFont(index));
-}
-
-// Critical Native
-static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-
-    uint64_t result = font->style().weight();
-    result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
-    result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
-    result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
-    return result;
-}
-
-// Critical Native
-static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-    const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
-    uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
-    return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
-}
-
-// FastNative
-static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-    const std::string& filePath = minikinSkia->getFilePath();
-    if (filePath.empty()) {
-        return nullptr;
-    }
-    return env->NewStringUTF(filePath.c_str());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const JNINativeMethod gNativeFontMethods[] = {
-    { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount },
-    { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily },
-    { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList },
-    { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount },
-    { "nGetFont", "(JI)J", (void*) NativeFont_getFont },
-    { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo },
-    { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo },
-    { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath },
-};
-
-int register_android_graphics_fonts_NativeFont(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods,
-            NELEM(gNativeFontMethods));
-}
-
-}  // namespace android
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9785aa5..a6fb958 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -23,13 +23,14 @@
 #include <set>
 #include <algorithm>
 
-#include "SkPaint.h"
-#include "SkTypeface.h"
 #include <hwui/MinikinSkia.h>
 #include <hwui/MinikinUtils.h>
 #include <hwui/Paint.h>
-#include <minikin/MinikinPaint.h>
 #include <minikin/MinikinFont.h>
+#include <minikin/MinikinPaint.h>
+#include "FontUtils.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
 
 namespace android {
 
@@ -149,7 +150,8 @@
 // CriticalNative
 static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
     const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
-    return reinterpret_cast<jlong>(layout->layout.getFont(i));
+    std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef)));
 }
 
 // CriticalNative
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 34df5dd..471a7f7 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -34,7 +34,7 @@
 }
 
 static inline SkScalar isIntegerAligned(SkScalar x) {
-    return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+    return MathUtils::isZero(roundf(x) - x);
 }
 
 // Disable filtering when there is no scaling in screen coordinates and the corners have the same
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 3b8caeb..7cfccb5 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -52,7 +52,7 @@
         RenderNodeDrawable* childNode = mChildren[drawIndex];
         SkASSERT(childNode);
         const float casterZ = childNode->getNodeProperties().getZ();
-        if (casterZ >= -NON_ZERO_EPSILON) {  // draw only children with negative Z
+        if (casterZ >= -MathUtils::NON_ZERO_EPSILON) {  // draw only children with negative Z
             return;
         }
         SkAutoCanvasRestore acr(canvas, true);
@@ -86,7 +86,7 @@
 
     const size_t endIndex = zChildren.size();
     while (drawIndex < endIndex  // draw only children with positive Z
-           && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON)
+           && zChildren[drawIndex]->getNodeProperties().getZ() <= MathUtils::NON_ZERO_EPSILON)
         drawIndex++;
     size_t shadowIndex = drawIndex;
 
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 85924c5..d998e50 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -101,7 +101,8 @@
         return;
     }
 
-    mGrContext->flushAndSubmit();
+    // flush and submit all work to the gpu and wait for it to finish
+    mGrContext->flushAndSubmit(/*syncCpu=*/true);
 
     switch (mode) {
         case TrimMemoryMode::Complete:
@@ -119,11 +120,6 @@
             SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
             break;
     }
-
-    // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually
-    // happen.
-    mGrContext->flush({});
-    mGrContext->submit(true);
 }
 
 void CacheManager::trimStaleResources() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 37a6ee7..65afcc3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -108,7 +108,6 @@
     rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
     mProfiler.setDensity(DeviceInfo::getDensity());
-    setRenderAheadDepth(Properties::defaultRenderAhead);
 }
 
 CanvasContext::~CanvasContext() {
@@ -157,24 +156,17 @@
 void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
     ATRACE_CALL();
 
-    if (mFixedRenderAhead) {
-        mRenderAheadCapacity = mRenderAheadDepth;
-    } else {
-        if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
-            mRenderAheadCapacity = 1;
-        } else {
-            mRenderAheadCapacity = 0;
-        }
-    }
-
     if (window) {
+        int extraBuffers = 0;
+        native_window_get_extra_buffer_count(window, &extraBuffers);
+
         mNativeSurface = std::make_unique<ReliableSurface>(window);
         mNativeSurface->init();
         if (enableTimeout) {
             // TODO: Fix error handling & re-shorten timeout
             ANativeWindow_setDequeueTimeout(window, 4000_ms);
         }
-        mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+        mNativeSurface->setExtraBufferCount(extraBuffers);
     } else {
         mNativeSurface = nullptr;
     }
@@ -441,24 +433,6 @@
     mRenderThread.pushBackFrameCallback(this);
 }
 
-void CanvasContext::setPresentTime() {
-    int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
-    int renderAhead = 0;
-    const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
-    if (mFixedRenderAhead) {
-        renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
-    } else if (frameIntervalNanos < 15_ms) {
-        renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
-    }
-
-    if (renderAhead) {
-        presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
-                (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
-                (frameIntervalNanos / 2);
-    }
-    native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
-}
-
 void CanvasContext::draw() {
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
@@ -478,8 +452,6 @@
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
     Frame frame = mRenderPipeline->getFrame();
-    setPresentTime();
-
     SkRect windowDirty = computeDirtyRect(frame, &dirty);
 
     bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
@@ -765,19 +737,6 @@
     return width != mLastFrameWidth || height != mLastFrameHeight;
 }
 
-void CanvasContext::setRenderAheadDepth(int renderAhead) {
-    if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
-        return;
-    }
-    if (renderAhead == -1) {
-        mFixedRenderAhead = false;
-        mRenderAheadDepth = 0;
-    } else {
-        mFixedRenderAhead = true;
-        mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
-    }
-}
-
 SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
     if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
         // can't rely on prior content of window if viewport size changes
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cc4eb32..b31883b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -193,9 +193,6 @@
         return mUseForceDark;
     }
 
-    // Must be called before setSurface
-    void setRenderAheadDepth(int renderAhead);
-
     SkISize getNextFrameSize() const;
 
 private:
@@ -211,7 +208,6 @@
 
     bool isSwapChainStuffed();
     bool surfaceRequiresRedraw();
-    void setPresentTime();
     void setupPipelineSurface();
 
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -232,9 +228,6 @@
     // painted onto its surface.
     bool mIsDirty = false;
     SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
-    bool mFixedRenderAhead = false;
-    uint32_t mRenderAheadDepth = 0;
-    uint32_t mRenderAheadCapacity = 0;
     struct SwapHistory {
         SkRect damage;
         nsecs_t vsyncTime;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b51f6dc..0ade8dd 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -295,11 +295,6 @@
     mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
 }
 
-void RenderProxy::setRenderAheadDepth(int renderAhead) {
-    mRenderThread.queue().post(
-            [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
-}
-
 int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
                                  SkBitmap* bitmap) {
     auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 33dabc9..a4adb16 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,23 +123,6 @@
     void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     void setForceDark(bool enable);
 
-    /**
-     * Sets a render-ahead depth on the backing renderer. This will increase latency by
-     * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
-     * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
-     *
-     * Not recommended to use in response to anything touch driven, but for canned animations
-     * where latency is not a concern careful use may be beneficial.
-     *
-     * Note that when increasing this there will be a frame gap of N frames where N is
-     * renderAhead - <current renderAhead>. When decreasing this if there are any pending
-     * frames they will retain their prior renderAhead value, so it will take a few frames
-     * for the decrease to flush through.
-     *
-     * @param renderAhead How far to render ahead, must be in the range [0..2]
-     */
-    void setRenderAheadDepth(int renderAhead);
-
     static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
                                            int bottom, SkBitmap* bitmap);
     static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 06f158f..6a9a98d 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -40,9 +40,9 @@
     return info;
 }
 
-const DisplayConfig& getActiveDisplayConfig() {
-    static DisplayConfig config = [] {
-        DisplayConfig config;
+const ui::DisplayMode& getActiveDisplayMode() {
+    static ui::DisplayMode config = [] {
+        ui::DisplayMode config;
 #if HWUI_NULL_GPU
         config.resolution = ui::Size(1080, 1920);
         config.xDpi = config.yDpi = 320.f;
@@ -51,7 +51,7 @@
         const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
         LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
 
-        const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config);
+        const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config);
         LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__);
 #endif
         return config;
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index a012ecb..7d2f6d8 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,8 +23,8 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <utils/Looper.h>
 
 #include <atomic>
@@ -37,10 +37,10 @@
 namespace test {
 
 const DisplayInfo& getDisplayInfo();
-const DisplayConfig& getActiveDisplayConfig();
+const ui::DisplayMode& getActiveDisplayMode();
 
 inline const ui::Size& getActiveDisplayResolution() {
-    return getActiveDisplayConfig().resolution;
+    return getActiveDisplayMode().resolution;
 }
 
 class TestContext {
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 74a039b..91022cf 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -38,7 +38,6 @@
         int count = 0;
         int reportFrametimeWeight = 0;
         bool renderOffscreen = true;
-        int renderAhead = 0;
     };
 
     template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index eda5d22..8c7d261 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -153,11 +153,6 @@
     proxy->resetProfileInfo();
     proxy->fence();
 
-    if (opts.renderAhead) {
-        usleep(33000);
-    }
-    proxy->setRenderAheadDepth(opts.renderAhead);
-
     ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
 
     nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 88d33c3..174a140 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -69,7 +69,6 @@
                        are offscreen rendered
   --benchmark_format   Set output format. Possible values are tabular, json, csv
   --renderer=TYPE      Sets the render pipeline to use. May be skiagl or skiavk
-  --render-ahead=NUM   Sets how far to render-ahead. Must be 0 (default), 1, or 2.
 )");
 }
 
@@ -171,7 +170,6 @@
     Onscreen,
     Offscreen,
     Renderer,
-    RenderAhead,
 };
 }
 
@@ -187,7 +185,6 @@
         {"onscreen", no_argument, nullptr, LongOpts::Onscreen},
         {"offscreen", no_argument, nullptr, LongOpts::Offscreen},
         {"renderer", required_argument, nullptr, LongOpts::Renderer},
-        {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead},
         {0, 0, 0, 0}};
 
 static const char* SHORT_OPTIONS = "c:r:h";
@@ -286,16 +283,6 @@
                 gOpts.renderOffscreen = true;
                 break;
 
-            case LongOpts::RenderAhead:
-                if (!optarg) {
-                    error = true;
-                }
-                gOpts.renderAhead = atoi(optarg);
-                if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) {
-                    error = true;
-                }
-                break;
-
             case 'h':
                 printHelp();
                 exit(EXIT_SUCCESS);
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 62bf39c..1d3f9d7 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -22,11 +22,11 @@
 namespace android {
 namespace uirenderer {
 
-#define NON_ZERO_EPSILON (0.001f)
-#define ALPHA_EPSILON (0.001f)
-
 class MathUtils {
 public:
+    static constexpr float NON_ZERO_EPSILON = 0.001f;
+    static constexpr float ALPHA_EPSILON = 0.001f;
+
     /**
      * Check for floats that are close enough to zero.
      */
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
new file mode 100644
index 0000000..67f407f
--- /dev/null
+++ b/libs/tracingproxy/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+    name: "libtracingproxy",
+
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "frameworks/base/core/java",
+        ],
+    },
+
+    srcs: [
+        ":ITracingServiceProxy.aidl",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 65721cc..adf58da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -36,6 +36,7 @@
 import android.location.Location;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.os.Bundle;
 import android.os.ICancellationSignal;
@@ -91,6 +92,9 @@
     void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
+    void addProviderRequestListener(in IProviderRequestListener listener);
+    void removeProviderRequestListener(in IProviderRequestListener listener);
+
     int getGnssBatchSize();
     void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
     void flushGnssBatch();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d569482..088b789 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -49,7 +49,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.location.provider.ProviderRequest.Listener;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -436,6 +439,9 @@
                 new GnssNavigationTransportManager();
     }
 
+    private static final ProviderRequestTransportManager sProviderRequestListeners =
+            new ProviderRequestTransportManager();
+
     private final Context mContext;
     private final ILocationManager mService;
 
@@ -2772,6 +2778,37 @@
     }
 
     /**
+     * Registers a {@link ProviderRequest.Listener} to all providers.
+     *
+     * @param executor the executor that the callback runs on
+     * @param listener the listener to register
+     * @return {@code true} always
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public boolean registerProviderRequestListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Listener listener) {
+        sProviderRequestListeners.addListener(listener,
+                new ProviderRequestTransport(executor, listener));
+        return true;
+    }
+
+    /**
+     * Unregisters a {@link ProviderRequest.Listener}.
+     *
+     * @param listener the listener to remove.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public void unregisterProviderRequestListener(
+            @NonNull Listener listener) {
+        sProviderRequestListeners.removeListener(listener);
+    }
+
+    /**
      * Returns the batch size (in number of Location objects) that are supported by the batching
      * interface.
      *
@@ -2960,6 +2997,22 @@
         }
     }
 
+    private static class ProviderRequestTransportManager extends
+            ListenerTransportManager<ProviderRequestTransport> {
+
+        @Override
+        protected void registerTransport(ProviderRequestTransport transport)
+                throws RemoteException {
+            getService().addProviderRequestListener(transport);
+        }
+
+        @Override
+        protected void unregisterTransport(ProviderRequestTransport transport)
+                throws RemoteException {
+            getService().removeProviderRequestListener(transport);
+        }
+    }
+
     private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
             ListenerExecutor, CancellationSignal.OnCancelListener {
 
@@ -3359,6 +3412,36 @@
         }
     }
 
+    private static class ProviderRequestTransport extends IProviderRequestListener.Stub
+            implements ListenerTransport<ProviderRequest.Listener> {
+
+        private final Executor mExecutor;
+
+        private volatile @Nullable ProviderRequest.Listener mListener;
+
+        ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null callback");
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void unregister() {
+            mListener = null;
+        }
+
+        @Override
+        public @Nullable ProviderRequest.Listener getListener() {
+            return mListener;
+        }
+
+        @Override
+        public void onProviderRequestChanged(String provider, ProviderRequest request) {
+            execute(mExecutor, listener -> listener.onProviderRequestChanged(provider, request));
+        }
+    }
+
     private static class BatchedLocationCallbackWrapper implements LocationListener {
 
         private final BatchedLocationCallback mCallback;
diff --git a/tools/hiddenapi/Android.bp b/location/java/android/location/provider/IProviderRequestListener.aidl
similarity index 60%
copy from tools/hiddenapi/Android.bp
copy to location/java/android/location/provider/IProviderRequestListener.aidl
index e0eb06cb..89d454a 100644
--- a/tools/hiddenapi/Android.bp
+++ b/location/java/android/location/provider/IProviderRequestListener.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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
+ *     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,
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
+package android.location.provider;
+
+import android.location.provider.ProviderRequest;
+
+/**
+ * {@hide}
+ */
+oneway interface IProviderRequestListener {
+    void onProviderRequestChanged(String provider, in ProviderRequest request);
 }
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index e543b04..b6ec323 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -53,6 +53,17 @@
     private final boolean mLocationSettingsIgnored;
     private final WorkSource mWorkSource;
 
+    /**
+     * Listener to be invoked when a new request is set to the provider.
+     */
+    public interface Listener {
+
+        /**
+         * Invoked when a new request is set.
+         */
+        void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
+    }
+
     private ProviderRequest(
             long intervalMillis,
             @Quality int quality,
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76..205c1f4 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@
 
     /**
      * @hide
-     * @return the internal device tyoe
+     * @return the internal device type
      */
     public int getInternalType() {
         return mPort.type();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 08deb15..8d090f8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1,5 +1,4 @@
 /*
-/*
  * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 812dac9..27f72687 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,6 +20,7 @@
 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -195,6 +196,8 @@
 
     private int mDeviceId;
 
+    private int mSessionId;
+
     /**
      * Never use without initializing parameters afterwards
      */
@@ -207,7 +210,10 @@
      * @hide
      */
     public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
-        if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
+        if (DEBUG) {
+            Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
+                    + " sessionId=" + pic.mSessionId);
+        }
         mPlayerIId = piid;
         mPlayerType = pic.mPlayerType;
         mClientUid = uid;
@@ -220,6 +226,7 @@
         } else {
             mIPlayerShell = null;
         }
+        mSessionId = pic.mSessionId;
     }
 
     /**
@@ -259,6 +266,7 @@
         anonymCopy.mClientUid = PLAYER_UPID_INVALID;
         anonymCopy.mClientPid = PLAYER_UPID_INVALID;
         anonymCopy.mIPlayerShell = null;
+        anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
         return anonymCopy;
     }
 
@@ -303,6 +311,17 @@
 
     /**
      * @hide
+     * Return the audio session ID associated with this player.
+     * See {@link AudioManager#generateAudioSessionId()}.
+     * @return an audio session ID
+     */
+    @SystemApi
+    public @IntRange(from = 0) int getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * @hide
      * Return the type of player linked to this configuration.
      * <br>Note that player types not exposed in the system API will be represented as
      * {@link #PLAYER_TYPE_UNKNOWN}.
@@ -381,6 +400,17 @@
 
     /**
      * @hide
+     * Handle a change of audio session Id
+     * @param sessionId the audio session ID
+     */
+    public boolean handleSessionIdEvent(int sessionId) {
+        final boolean changed = sessionId != mSessionId;
+        mSessionId = sessionId;
+        return changed;
+    }
+
+    /**
+     * @hide
      * Handle a player state change
      * @param event
      * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
@@ -476,7 +506,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid,
+                mSessionId);
     }
 
     @Override
@@ -498,6 +529,7 @@
             ips = mIPlayerShell;
         }
         dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
+        dest.writeInt(mSessionId);
     }
 
     private AudioPlaybackConfiguration(Parcel in) {
@@ -510,6 +542,7 @@
         mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
         final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
         mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
+        mSessionId = in.readInt();
     }
 
     @Override
@@ -523,7 +556,8 @@
                 && (mDeviceId == that.mDeviceId)
                 && (mPlayerType == that.mPlayerType)
                 && (mClientUid == that.mClientUid)
-                && (mClientPid == that.mClientPid));
+                && (mClientPid == that.mClientPid))
+                && (mSessionId == that.mSessionId);
     }
 
     @Override
@@ -533,7 +567,8 @@
                 + " type:" + toLogFriendlyPlayerType(mPlayerType)
                 + " u/pid:" + mClientUid + "/" + mClientPid
                 + " state:" + toLogFriendlyPlayerState(mPlayerState)
-                + " attr:" + mPlayerAttr;
+                + " attr:" + mPlayerAttr
+                + " sessionId:" + mSessionId;
     }
 
     //=====================================================================
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 175d36f..e056d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -836,7 +836,7 @@
             mState = STATE_INITIALIZED;
         }
 
-        baseRegisterPlayer();
+        baseRegisterPlayer(mSessionId);
     }
 
     /**
@@ -866,7 +866,7 @@
 
         // other initialization...
         if (nativeTrackInJavaObj != 0) {
-            baseRegisterPlayer();
+            baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
             deferred_connect(nativeTrackInJavaObj);
         } else {
             mState = STATE_UNINITIALIZED;
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 06bf5f7..9c6b276 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,9 +119,14 @@
      */
     public static final int QUALITY_2K = 12;
 
+    /**
+     * Quality level corresponding to 8K UHD (7680 x 4320) resolution
+     */
+    public static final int QUALITY_8KUHD = 13;
+
     // Start and end of quality list
     private static final int QUALITY_LIST_START = QUALITY_LOW;
-    private static final int QUALITY_LIST_END = QUALITY_2K;
+    private static final int QUALITY_LIST_END = QUALITY_8KUHD;
 
     /**
      * Time lapse quality level corresponding to the lowest available resolution.
@@ -188,10 +193,14 @@
      */
     public static final int QUALITY_TIME_LAPSE_2K = 1012;
 
+    /**
+     * Time lapse quality level corresponding to the 8K UHD (7680 x 4320) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_8KUHD = 1013;
 
     // Start and end of timelapse quality list
     private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW;
-    private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K;
+    private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_8KUHD;
 
     /**
      * High speed ( >= 100fps) quality level corresponding to the lowest available resolution.
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a..e339ae8 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -54,7 +54,7 @@
         Preconditions.checkArgument(device.isSource(), "Requires a source device");
         mAudioDeviceInfo = device;
         mAudioAttributes = attributes;
-        baseRegisterPlayer();
+        baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
     }
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dd42aab..71ee57e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -73,6 +73,8 @@
 
     oneway void releaseRecorder(in int riid);
 
+    oneway void playerSessionId(in int piid, in int sessionId);
+
     // Java-only methods below.
 
     oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd0bc61..ca0d29f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -663,6 +663,10 @@
      * result in an exception.</p>
      */
     public MediaPlayer() {
+        this(AudioSystem.AUDIO_SESSION_ALLOCATE);
+    }
+
+    private MediaPlayer(int sessionId) {
         super(new AudioAttributes.Builder().build(),
                 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
 
@@ -684,7 +688,7 @@
         native_setup(new WeakReference<MediaPlayer>(this),
                 getCurrentOpPackageName());
 
-        baseRegisterPlayer();
+        baseRegisterPlayer(sessionId);
     }
 
     /*
@@ -913,7 +917,7 @@
             AudioAttributes audioAttributes, int audioSessionId) {
 
         try {
-            MediaPlayer mp = new MediaPlayer();
+            MediaPlayer mp = new MediaPlayer(audioSessionId);
             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                 new AudioAttributes.Builder().build();
             mp.setAudioAttributes(aa);
@@ -978,7 +982,7 @@
             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
             if (afd == null) return null;
 
-            MediaPlayer mp = new MediaPlayer();
+            MediaPlayer mp = new MediaPlayer(audioSessionId);
 
             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                 new AudioAttributes.Builder().build();
@@ -2365,7 +2369,13 @@
      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
      * @throws IllegalStateException if it is called in an invalid state
      */
-    public native void setAudioSessionId(int sessionId)  throws IllegalArgumentException, IllegalStateException;
+    public void setAudioSessionId(int sessionId)
+            throws IllegalArgumentException, IllegalStateException {
+        native_setAudioSessionId(sessionId);
+        baseUpdateSessionId(sessionId);
+    }
+
+    private native void native_setAudioSessionId(int sessionId);
 
     /**
      * Returns the audio session ID.
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279..4407efa 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -97,6 +97,7 @@
      * Constructor. Must be given audio attributes, as they are required for AppOps.
      * @param attr non-null audio attributes
      * @param class non-null class of the implementation of this abstract class
+     * @param sessionId the audio session Id
      */
     PlayerBase(@NonNull AudioAttributes attr, int implType) {
         if (attr == null) {
@@ -110,7 +111,7 @@
     /**
      * Call from derived class when instantiation / initialization is successful
      */
-    protected void baseRegisterPlayer() {
+    protected void baseRegisterPlayer(int sessionId) {
         if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
             IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
             mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -128,7 +129,8 @@
         }
         try {
             mPlayerIId = getService().trackPlayer(
-                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
+                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
+                            sessionId));
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
         }
@@ -145,7 +147,7 @@
         try {
             getService().playerAttributes(mPlayerIId, attr);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
+            Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);
         }
         synchronized (mLock) {
             boolean attributesChanged = (mAttributes != attr);
@@ -154,6 +156,18 @@
         }
     }
 
+    /**
+     * To be called whenever the session ID of the player changes
+     * @param sessionId, the new session Id
+     */
+    void baseUpdateSessionId(int sessionId) {
+        try {
+            getService().playerSessionId(mPlayerIId, sessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e);
+        }
+    }
+
     void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
         int deviceId = 0;
         if (deviceInfo != null) {
@@ -566,16 +580,19 @@
         public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
         public final AudioAttributes mAttributes;
         public final IPlayer mIPlayer;
+        public final int mSessionId;
 
-        PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
+        PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer,
+                     int sessionId) {
             mPlayerType = type;
             mAttributes = attr;
             mIPlayer = iplayer;
+            mSessionId = sessionId;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mPlayerType);
+            return Objects.hash(mPlayerType, mSessionId);
         }
 
         @Override
@@ -588,6 +605,7 @@
             dest.writeInt(mPlayerType);
             mAttributes.writeToParcel(dest, 0);
             dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
+            dest.writeInt(mSessionId);
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
@@ -611,6 +629,7 @@
             // IPlayer can be null if unmarshalling a Parcel coming from who knows where
             final IBinder b = in.readStrongBinder();
             mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
+            mSessionId = in.readInt();
         }
 
         @Override
@@ -621,7 +640,8 @@
             PlayerIdCard that = (PlayerIdCard) o;
 
             // FIXME change to the binder player interface once supported as a member
-            return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
+            return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)
+                    && (mSessionId == that.mSessionId));
         }
     }
 
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf3..32413dc 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -155,7 +155,8 @@
         }
         mAttributes = attributes;
 
-        baseRegisterPlayer();
+        // FIXME: b/174876164 implement session id for soundpool
+        baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
     }
 
     /**
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 31fb8d0..9bf126b 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
-    void setMediaButtonReceiver(in PendingIntent mbr);
+    void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
     void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
     void setLaunchPendingIntent(in PendingIntent pi);
     void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 24118b0..20fa53d 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
     @Deprecated
     public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
         try {
-            mBinder.setMediaButtonReceiver(mbr);
+            mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
         }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 8525e99..ee0be01 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -65,6 +65,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -245,6 +246,8 @@
     private static final int MSG_ON_FILTER_STATUS = 3;
     private static final int MSG_ON_LNB_EVENT = 4;
 
+    private static final int FILTER_CLEANUP_THRESHOLD = 256;
+
     /** @hide */
     @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
     @Retention(RetentionPolicy.SOURCE)
@@ -1208,6 +1211,15 @@
             synchronized (mFilters) {
                 WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
                 mFilters.add(weakFilter);
+                if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) {
+                    Iterator<WeakReference<Filter>> iterator = mFilters.iterator();
+                    while (iterator.hasNext()) {
+                        WeakReference<Filter> wFilter = iterator.next();
+                        if (wFilter.get() == null) {
+                            iterator.remove();
+                        }
+                    }
+                }
             }
         }
         return filter;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 67a2c49..65b64d7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,8 +31,8 @@
     ],
 
     shared_libs: [
-        "audioclient-types-aidl-unstable-cpp",
-        "av-types-aidl-unstable-cpp",
+        "audioclient-types-aidl-cpp",
+        "av-types-aidl-cpp",
         "libandroid_runtime",
         "libaudioclient",
         "libnativehelper",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index bd8d2e9..98ac5b9 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1409,7 +1409,7 @@
     {"native_setup",        "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
-    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
+    {"native_setAudioSessionId",   "(I)V",                      (void *)android_media_MediaPlayer_set_audio_session_id},
     {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 31eb35a..c38a8fa 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -100,6 +100,11 @@
      */
     Result close();
 
+    /**
+     * Get the Aidl demux to set as source.
+     */
+    shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; }
+
     void setId(int id) { mId = id; }
     int getId() { return mId; }
 
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
index 979beea..c9bacda 100644
--- a/media/jni/tuner/DescramblerClient.cpp
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -27,13 +27,12 @@
 
 /////////////// DescramblerClient ///////////////////////
 
-// TODO: pending aidl interface
-DescramblerClient::DescramblerClient() {
-    //mTunerDescrambler = tunerDescrambler;
+DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) {
+    mTunerDescrambler = tunerDescrambler;
 }
 
 DescramblerClient::~DescramblerClient() {
-    //mTunerDescrambler = NULL;
+    mTunerDescrambler = NULL;
     mDescrambler = NULL;
 }
 
@@ -47,7 +46,10 @@
         return Result::INVALID_ARGUMENT;
     }
 
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->setDemuxSource(demuxClient->getId());
@@ -57,7 +59,10 @@
 }
 
 Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->setKeyToken(keyToken);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->setKeyToken(keyToken);
@@ -67,7 +72,11 @@
 }
 
 Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->addPid(
+                getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
@@ -76,16 +85,24 @@
     return Result::INVALID_STATE;}
 
 Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
-    // TODO: pending aidl interface
-
-    if (mDescrambler != NULL) {
-        return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->removePid(
+                getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
-    return Result::INVALID_STATE;}
+    if (mDescrambler != NULL) {
+        return mDescrambler->removePid(pid, optionalSourceFilter->getHalFilter());
+    }
+
+    return Result::INVALID_STATE;
+}
 
 Result DescramblerClient::close() {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->close();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->close();
@@ -95,4 +112,16 @@
 
 /////////////// DescramblerClient Helper Methods ///////////////////////
 
+TunerDemuxPid DescramblerClient::getAidlDemuxPid(DemuxPid& pid) {
+    TunerDemuxPid aidlPid;
+    switch (pid.getDiscriminator()) {
+        case DemuxPid::hidl_discriminator::tPid:
+            aidlPid.set<TunerDemuxPid::tPid>((int)pid.tPid());
+            break;
+        case DemuxPid::hidl_discriminator::mmtpPid:
+            aidlPid.set<TunerDemuxPid::mmtpPid>((int)pid.mmtpPid());
+            break;
+    }
+    return aidlPid;
+}
 }  // namespace android
diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h
index 8af6883..a8fa1e2 100644
--- a/media/jni/tuner/DescramblerClient.h
+++ b/media/jni/tuner/DescramblerClient.h
@@ -17,14 +17,15 @@
 #ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
 #define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
 
-//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
+#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
 #include <android/hardware/tv/tuner/1.0/IDescrambler.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
 #include "DemuxClient.h"
 #include "FilterClient.h"
 
-//using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::TunerDemuxPid;
 
 using ::android::hardware::tv::tuner::V1_0::IDescrambler;
 using ::android::hardware::tv::tuner::V1_0::Result;
@@ -37,8 +38,7 @@
 struct DescramblerClient : public RefBase {
 
 public:
-    // TODO: pending hidl interface
-    DescramblerClient();
+    DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler);
     ~DescramblerClient();
 
     // TODO: remove after migration to Tuner Service is done.
@@ -70,12 +70,13 @@
     Result close();
 
 private:
+    TunerDemuxPid getAidlDemuxPid(DemuxPid& pid);
+
     /**
      * An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client
      * opens a descrambler. Default null when descrambler is not opened.
      */
-    // TODO: pending on aidl interface
-    //shared_ptr<ITunerDescrambler> mTunerDescrambler;
+    shared_ptr<ITunerDescrambler> mTunerDescrambler;
 
     /**
      * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 2aaf96c..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -35,7 +35,7 @@
 using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
 using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
 using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-
+using ::android::hardware::hidl_vec;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
@@ -703,10 +703,10 @@
 
 void TunerFilterCallback::getHidlMediaEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
                 .get<TunerFilterEvent::media>().avMemory));
-        event.events.resize(i + 1);
         event.events[i].media({
             .avMemory = handle,
             .streamId = static_cast<DemuxStreamId>(filterEvents[i]
@@ -752,9 +752,9 @@
 
 void TunerFilterCallback::getHidlSectionEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto section = filterEvents[i].get<TunerFilterEvent::section>();
-        event.events.resize(i + 1);
         event.events[i].section({
             .tableId = static_cast<uint16_t>(section.tableId),
             .version = static_cast<uint16_t>(section.version),
@@ -766,9 +766,9 @@
 
 void TunerFilterCallback::getHidlPesEvent(
         const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
-        event.events.resize(i + 1);
         event.events[i].pes({
             .streamId = static_cast<DemuxStreamId>(pes.streamId),
             .dataLength = static_cast<uint16_t>(pes.dataLength),
@@ -779,9 +779,10 @@
 
 void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+    event.events.resize(filterEvents.size());
+    eventExt.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
-        event.events.resize(i + 1);
         event.events[i].tsRecord({
             .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
             .byteNumber = static_cast<uint64_t>(ts.byteNumber),
@@ -803,7 +804,6 @@
                 break;
         }
 
-        eventExt.events.resize(i + 1);
         if (ts.isExtended) {
             eventExt.events[i].tsRecord({
                 .pts = static_cast<uint64_t>(ts.pts),
@@ -817,15 +817,15 @@
 
 void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+    event.events.resize(filterEvents.size());
+    eventExt.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
-        event.events.resize(i + 1);
         event.events[i].mmtpRecord({
             .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
             .byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
         });
 
-        eventExt.events.resize(i + 1);
         if (mmtp.isExtended) {
             eventExt.events[i].mmtpRecord({
                 .pts = static_cast<uint64_t>(mmtp.pts),
@@ -841,9 +841,9 @@
 
 void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto download = filterEvents[i].get<TunerFilterEvent::download>();
-        event.events.resize(i + 1);
         event.events[i].download({
             .itemId = static_cast<uint32_t>(download.itemId),
             .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
@@ -856,9 +856,9 @@
 
 void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
-        event.events.resize(i + 1);
         event.events[i].ipPayload({
             .dataLength = static_cast<uint16_t>(ip.dataLength),
         });
@@ -867,15 +867,15 @@
 
 void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
         DemuxFilterEvent& event) {
+    event.events.resize(filterEvents.size());
     for (int i = 0; i < filterEvents.size(); i++) {
         auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
-        event.events.resize(i + 1);
         event.events[i].temi({
             .pts = static_cast<uint64_t>(temi.pts),
             .descrTag = static_cast<uint8_t>(temi.descrTag),
         });
-        vector<uint8_t> descrData(temi.descrData.size());
-        copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+        hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
+        event.events[i].temi().descrData = descrData;
     }
 }
 
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 08573a6..3a00133 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -27,23 +27,43 @@
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
 using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
 using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
 using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
 using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
 using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
 using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
 using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendType;
 
 namespace android {
 
@@ -71,8 +91,8 @@
     if (mTunerFrontend != NULL) {
         mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
         mAidlCallback->setFrontendType(mType);
-        mTunerFrontend->setCallback(mAidlCallback);
-        return Result::SUCCESS;
+        Status s = mTunerFrontend->setCallback(mAidlCallback);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     mHidlCallback = new HidlFrontendCallback(frontendClientCallback);
@@ -160,9 +180,16 @@
     vector<FrontendStatus> status;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*status = mTunerFrontend->getStatus(statusTypes);
-        return status;*/
+        vector<TunerFrontendStatus> aidlStatus;
+        vector<int> types;
+        for (auto t : statusTypes) {
+            types.push_back((int)t);
+        }
+        Status s = mTunerFrontend->getStatus(types, &aidlStatus);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return status;
+        }
+        return getHidlStatus(aidlStatus);
     }
 
     if (mFrontend != NULL && statusTypes.size() > 0) {
@@ -180,14 +207,22 @@
 
     return status;
 }
+
 vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
         vector<FrontendStatusTypeExt1_1> statusTypes) {
     vector<FrontendStatusExt1_1> status;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
-        return status;*/
+        vector<TunerFrontendStatus> aidlStatus;
+        vector<int> types;
+        for (auto t : statusTypes) {
+            types.push_back((int)t);
+        }
+        Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return status;
+        }
+        return getHidlStatusExt(aidlStatus);
     }
 
     if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
@@ -208,9 +243,8 @@
 
 Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->setLnb(lnbClient->getAidlLnb());
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb());
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend != NULL) {
@@ -223,9 +257,8 @@
 
 Result FrontendClient::setLna(bool bEnable) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->setLna(bEnable);
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->setLna(bEnable);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend != NULL) {
@@ -240,9 +273,11 @@
     int ltsId = (int)Constant::INVALID_LTS_ID;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId);
-        return ltsId;*/
+        Status s = mTunerFrontend->linkCiCamToFrontend(ciCamId, &ltsId);
+        if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+            return ltsId;
+        }
+        return (int)Constant::INVALID_LTS_ID;
     }
 
     if (mFrontend_1_1 != NULL) {
@@ -262,9 +297,8 @@
 
 Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend_1_1 != NULL) {
@@ -302,6 +336,400 @@
     return mId;
 }
 
+vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
+    vector<FrontendStatus> hidlStatus;
+    for (TunerFrontendStatus s : aidlStatus) {
+        FrontendStatus status;
+        switch (s.getTag()) {
+            case TunerFrontendStatus::isDemodLocked: {
+                status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::snr: {
+                status.snr(s.get<TunerFrontendStatus::snr>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::ber: {
+                status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::per: {
+                status.per((uint32_t)s.get<TunerFrontendStatus::per>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::preBer: {
+                status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::signalQuality: {
+                status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::signalStrength: {
+                status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::symbolRate: {
+                status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::innerFec: {
+                status.innerFec(static_cast<FrontendInnerFec>(
+                        s.get<TunerFrontendStatus::innerFec>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::modulation: {
+                auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+                switch (mType) {
+                    case (int)FrontendType::DVBC:
+                        status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBS:
+                        status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS:
+                        status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS3:
+                        status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::inversion: {
+                status.inversion(static_cast<FrontendDvbcSpectralInversion>(
+                        s.get<TunerFrontendStatus::inversion>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::lnbVoltage: {
+                status.lnbVoltage(static_cast<LnbVoltage>(
+                        s.get<TunerFrontendStatus::lnbVoltage>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::plpId: {
+                status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isEWBS: {
+                status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::agc: {
+                status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLnaOn: {
+                status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLayerError: {
+                auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
+                hidl_vec<bool> e(aidlE.begin(), aidlE.end());
+                status.isLayerError(e);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::mer: {
+                status.mer(s.get<TunerFrontendStatus::mer>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::freqOffset: {
+                status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::hierarchy: {
+                status.hierarchy(static_cast<FrontendDvbtHierarchy>(
+                        s.get<TunerFrontendStatus::freqOffset>()));
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isRfLocked: {
+                status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::plpInfo: {
+                int size = s.get<TunerFrontendStatus::plpInfo>().size();
+                status.plpInfo().resize(size);
+                for (int i = 0; i < size; i++) {
+                    auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
+                    status.plpInfo()[i] = {
+                        .plpId = (uint8_t)aidlInfo.plpId,
+                        .isLocked = aidlInfo.isLocked,
+                        .uec = (uint32_t)aidlInfo.uec,
+                    };
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return hidlStatus;
+}
+
+vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
+        vector<TunerFrontendStatus>& aidlStatus) {
+    vector<FrontendStatusExt1_1> hidlStatus;
+    for (TunerFrontendStatus s : aidlStatus) {
+        FrontendStatusExt1_1 status;
+        switch (s.getTag()) {
+            case TunerFrontendStatus::modulations: {
+                for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
+                    int size = status.modulations().size();
+                    status.modulations().resize(size + 1);
+                    switch (mType) {
+                        case (int)FrontendType::DVBC:
+                            status.modulations()[size].dvbc(
+                                    static_cast<FrontendDvbcModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DVBS:
+                            status.modulations()[size].dvbs(
+                                    static_cast<FrontendDvbsModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DVBT:
+                            status.modulations()[size].dvbt(
+                                    static_cast<FrontendDvbtConstellation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBS:
+                            status.modulations()[size].isdbs(
+                                    static_cast<FrontendIsdbsModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBS3:
+                            status.modulations()[size].isdbs3(
+                                    static_cast<FrontendIsdbs3Modulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ISDBT:
+                            status.modulations()[size].isdbt(
+                                    static_cast<FrontendIsdbtModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ATSC:
+                            status.modulations()[size].atsc(
+                                    static_cast<FrontendAtscModulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::ATSC3:
+                            status.modulations()[size].atsc3(
+                                    static_cast<FrontendAtsc3Modulation>(aidlMod));
+                            break;
+                        case (int)FrontendType::DTMB:
+                            status.modulations()[size].dtmb(
+                                    static_cast<FrontendDtmbModulation>(aidlMod));
+                            break;
+                        default:
+                            status.modulations().resize(size);
+                            break;
+                    }
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::bers: {
+                auto aidlB = s.get<TunerFrontendStatus::bers>();
+                hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
+                status.bers(b);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::codeRates: {
+                int size = s.get<TunerFrontendStatus::codeRates>().size();
+                status.codeRates().resize(size);
+                for (int i = 0; i < size; i++) {
+                    auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
+                    status.codeRates()[i] =
+                            static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::bandwidth: {
+                auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+                switch (mType) {
+                    case (int)FrontendType::ATSC3:
+                        status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBC:
+                        status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DVBT:
+                        status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::interval: {
+                auto aidlInter = s.get<TunerFrontendStatus::interval>();
+                switch (mType) {
+                    case (int)FrontendType::DVBT:
+                        status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::transmissionMode: {
+                auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+                switch (mType) {
+                    case (int)FrontendType::DVBT:
+                        status.transmissionMode().dvbt(
+                                static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBT:
+                        status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::DTMB:
+                        status.transmissionMode().dtmb(
+                                static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::uec: {
+                status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::systemId: {
+                status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::interleaving: {
+                for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
+                    int size = status.interleaving().size();
+                    status.interleaving().resize(size + 1);
+                    switch (mType) {
+                        case (int)FrontendType::DVBC:
+                            status.interleaving()[size].dvbc(
+                                    static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+                            break;
+                        case (int)FrontendType::ATSC3:
+                            status.interleaving()[size].atsc3(
+                                    static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+                            break;
+                        case (int)FrontendType::DTMB:
+                            status.interleaving()[size].dtmb(
+                                    static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+                            break;
+                        default:
+                            status.interleaving().resize(size);
+                            break;
+                    }
+                }
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isdbtSegment: {
+                auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
+                hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
+                status.isdbtSegment(s);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::tsDataRate: {
+                auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
+                hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
+                status.tsDataRate(ts);
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::rollOff: {
+                auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+                switch (mType) {
+                    case (int)FrontendType::DVBS:
+                        status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS:
+                        status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    case (int)FrontendType::ISDBS3:
+                        status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+                        hidlStatus.push_back(status);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            }
+            case TunerFrontendStatus::isMiso: {
+                status.isMiso(s.get<TunerFrontendStatus::isMiso>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isLinear: {
+                status.isLinear(s.get<TunerFrontendStatus::isLinear>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            case TunerFrontendStatus::isShortFrames: {
+                status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
+                hidlStatus.push_back(status);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+    return hidlStatus;
+}
+
 TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
         const FrontendSettingsExt1_1& settingsExt1_1) {
     bool isExtended = validateExtendedSettings(settingsExt1_1);
@@ -686,14 +1114,15 @@
             vector<TunerFrontendScanAtsc3PlpInfo> plp =
                     message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
             hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
-            for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+            int size = plp.size();
+            plpInfo.resize(size);
+            for (int i = 0; i < size; i++) {
+                auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
                 FrontendScanAtsc3PlpInfo p{
                     .plpId = static_cast<uint8_t>(info.plpId),
                     .bLlsFlag = info.llsFlag,
                 };
-                int size = plpInfo.size();
-                plpInfo.resize(size + 1);
-                plpInfo[size] = p;
+                plpInfo[i] = p;
             }
             scanMessage.atsc3PlpInfos(plpInfo);
             break;
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index b0107ff..298b397 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -43,6 +43,7 @@
 using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
 using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
 using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
 
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -182,6 +183,9 @@
     int getId();
 
 private:
+    vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
+    vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
+
     TunerFrontendSettings getAidlFrontendSettings(
             const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
     TunerFrontendAnalogSettings getAidlAnalogSettings(
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index e7869e8..465dc23 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -108,7 +108,7 @@
      */
     Result close();
 
-    //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
+    shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
     void setId(LnbId id) { mId = id; }
     LnbId getId() { return mId; }
 
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index a604490d..7f954b5 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -22,7 +22,10 @@
 
 #include "TunerClient.h"
 
+using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
 using ::android::hardware::tv::tuner::V1_0::FrontendType;
 
 namespace android {
@@ -136,12 +139,11 @@
 shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
     if (mTunerService != NULL) {
         TunerFrontendInfo aidlFrontendInfo;
-        // TODO: handle error code
         Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
         if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
             return NULL;
         }
-        return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo));
+        return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo));
     }
 
     if (mTuner != NULL) {
@@ -157,7 +159,22 @@
 }
 
 shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) {
-    // pending aidl interface
+    if (mTunerService != NULL) {
+        TunerFrontendDtmbCapabilities dtmbCaps;
+        Status s = mTunerService->getFrontendDtmbCapabilities(id, &dtmbCaps);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        FrontendDtmbCapabilities hidlCaps{
+            .transmissionModeCap = static_cast<uint32_t>(dtmbCaps.transmissionModeCap),
+            .bandwidthCap = static_cast<uint32_t>(dtmbCaps.bandwidthCap),
+            .modulationCap = static_cast<uint32_t>(dtmbCaps.modulationCap),
+            .codeRateCap = static_cast<uint32_t>(dtmbCaps.codeRateCap),
+            .guardIntervalCap = static_cast<uint32_t>(dtmbCaps.guardIntervalCap),
+            .interleaveModeCap = static_cast<uint32_t>(dtmbCaps.interleaveModeCap),
+        };
+        return make_shared<FrontendDtmbCapabilities>(hidlCaps);
+    }
 
     if (mTuner_1_1 != NULL) {
         Result result;
@@ -224,17 +241,18 @@
     return NULL;
 }
 
-sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) {
+sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) {
     if (mTunerService != NULL) {
-        // TODO: handle error code
-        /*shared_ptr<ITunerDescrambler> tunerDescrambler;
-        mTunerService->openDescrambler(demuxHandle, &tunerDescrambler);
-        return new DescramblerClient(tunerDescrambler);*/
+        shared_ptr<ITunerDescrambler> tunerDescrambler;
+        Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return new DescramblerClient(tunerDescrambler);
     }
 
     if (mTuner != NULL) {
-        // TODO: pending aidl interface
-        sp<DescramblerClient> descramblerClient = new DescramblerClient();
+        sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL);
         sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
         if (hidlDescrambler != NULL) {
             descramblerClient->setHidlDescrambler(hidlDescrambler);
@@ -486,7 +504,7 @@
     return caps;
 }
 
-FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
+FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
     FrontendInfo hidlFrontendInfo {
         .type = static_cast<FrontendType>(aidlFrontendInfo.type),
         .minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency),
@@ -496,8 +514,102 @@
         .acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange),
         .exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId),
     };
-    // TODO: handle Frontend caps
 
+    int size = aidlFrontendInfo.statusCaps.size();
+    hidlFrontendInfo.statusCaps.resize(size);
+    for (int i = 0; i < size; i++) {
+        hidlFrontendInfo.statusCaps[i] =
+                static_cast<FrontendStatusType>(aidlFrontendInfo.statusCaps[i]);
+    }
+
+    switch (aidlFrontendInfo.caps.getTag()) {
+        case TunerFrontendCapabilities::analogCaps: {
+            auto analog = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::analogCaps>();
+            hidlFrontendInfo.frontendCaps.analogCaps({
+                .typeCap = static_cast<uint32_t>(analog.typeCap),
+                .sifStandardCap = static_cast<uint32_t>(analog.sifStandardCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::atscCaps: {
+            auto atsc = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atscCaps>();
+            hidlFrontendInfo.frontendCaps.atscCaps({
+                .modulationCap = static_cast<uint32_t>(atsc.modulationCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::atsc3Caps: {
+            auto atsc3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atsc3Caps>();
+            hidlFrontendInfo.frontendCaps.atsc3Caps({
+                .bandwidthCap = static_cast<uint32_t>(atsc3.bandwidthCap),
+                .modulationCap = static_cast<uint32_t>(atsc3.modulationCap),
+                .timeInterleaveModeCap = static_cast<uint32_t>(atsc3.timeInterleaveModeCap),
+                .codeRateCap = static_cast<uint32_t>(atsc3.codeRateCap),
+                .fecCap = static_cast<uint32_t>(atsc3.fecCap),
+                .demodOutputFormatCap = static_cast<uint8_t>(atsc3.demodOutputFormatCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::cableCaps: {
+            auto cable = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::cableCaps>();
+            hidlFrontendInfo.frontendCaps.dvbcCaps({
+                .modulationCap = static_cast<uint32_t>(cable.modulationCap),
+                .fecCap = static_cast<uint64_t>(cable.codeRateCap),
+                .annexCap = static_cast<uint8_t>(cable.annexCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::dvbsCaps: {
+            auto dvbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbsCaps>();
+            hidlFrontendInfo.frontendCaps.dvbsCaps({
+                .modulationCap = static_cast<int32_t>(dvbs.modulationCap),
+                .innerfecCap = static_cast<uint64_t>(dvbs.codeRateCap),
+                .standard = static_cast<uint8_t>(dvbs.standard),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::dvbtCaps: {
+            auto dvbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbtCaps>();
+            hidlFrontendInfo.frontendCaps.dvbtCaps({
+                .transmissionModeCap = static_cast<uint32_t>(dvbt.transmissionModeCap),
+                .bandwidthCap = static_cast<uint32_t>(dvbt.bandwidthCap),
+                .constellationCap = static_cast<uint32_t>(dvbt.constellationCap),
+                .coderateCap = static_cast<uint32_t>(dvbt.codeRateCap),
+                .hierarchyCap = static_cast<uint32_t>(dvbt.hierarchyCap),
+                .guardIntervalCap = static_cast<uint32_t>(dvbt.guardIntervalCap),
+                .isT2Supported = dvbt.isT2Supported,
+                .isMisoSupported = dvbt.isMisoSupported,
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbsCaps: {
+            auto isdbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbsCaps>();
+            hidlFrontendInfo.frontendCaps.isdbsCaps({
+                .modulationCap = static_cast<uint32_t>(isdbs.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbs.codeRateCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbs3Caps: {
+            auto isdbs3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbs3Caps>();
+            hidlFrontendInfo.frontendCaps.isdbs3Caps({
+                .modulationCap = static_cast<uint32_t>(isdbs3.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbs3.codeRateCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbtCaps: {
+            auto isdbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbtCaps>();
+            hidlFrontendInfo.frontendCaps.isdbtCaps({
+                .modeCap = static_cast<uint32_t>(isdbt.modeCap),
+                .bandwidthCap = static_cast<uint32_t>(isdbt.bandwidthCap),
+                .modulationCap = static_cast<uint32_t>(isdbt.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbt.codeRateCap),
+                .guardIntervalCap = static_cast<uint32_t>(isdbt.guardIntervalCap),
+            });
+            break;
+        }
+    }
     return hidlFrontendInfo;
 }
 
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index acd018e..744bf20 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -147,7 +147,7 @@
     sp<IDescrambler> openHidlDescrambler();
     vector<int> getLnbHandles();
     DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
-    FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
+    FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
     void updateTunerResources();
     void updateFrontendResources();
     void updateLnbResources();
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 45f42f1b5..48d7380 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -264,7 +264,7 @@
                 static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
                 1  /* maxRun */);
 
-    const minikin::Font* font = runs[0].fakedFont.font;
+    const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
     std::unique_ptr<AFont> result = std::make_unique<AFont>();
     const android::MinikinFontSkia* minikinFontSkia =
             reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index fd71670..606cd57 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -322,7 +322,8 @@
 
     void onDeviceSelected(String callingPackage, String deviceAddress) {
         mServiceCallback.complete(new Association(
-                getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false));
+                getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false,
+                System.currentTimeMillis()));
     }
 
     void onCancel() {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index fa3213d..fbe15bb 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -21,6 +21,7 @@
 import static android.net.NetworkRequest.Type.LISTEN;
 import static android.net.NetworkRequest.Type.REQUEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
+import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 import static android.net.QosCallback.QosCallbackRegistrationException;
 
 import android.annotation.CallbackExecutor;
@@ -1368,7 +1369,7 @@
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
         try {
             return mService.getDefaultNetworkCapabilitiesForUser(
-                    userId, mContext.getOpPackageName());
+                    userId, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1450,7 +1451,8 @@
     @Nullable
     public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
-            return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+            return mService.getNetworkCapabilities(
+                    network, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3720,7 +3722,8 @@
         printStackTrace();
         checkCallbackNotNull(callback);
         Preconditions.checkArgument(
-                reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities");
+                reqType == TRACK_DEFAULT || reqType == TRACK_SYSTEM_DEFAULT || need != null,
+                "null NetworkCapabilities");
         final NetworkRequest request;
         final String callingPackageName = mContext.getOpPackageName();
         try {
@@ -3735,7 +3738,8 @@
                 Binder binder = new Binder();
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName);
+                            need, messenger, binder, callingPackageName,
+                            getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
@@ -4180,7 +4184,8 @@
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingListenForNetwork(
-                    request.networkCapabilities, operation, mContext.getOpPackageName());
+                    request.networkCapabilities, operation, mContext.getOpPackageName(),
+                    getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4189,8 +4194,9 @@
     }
 
     /**
-     * Registers to receive notifications about changes in the system default network. The callbacks
-     * will continue to be called until either the application exits or
+     * Registers to receive notifications about changes in the application's default network. This
+     * may be a physical network or a virtual network, such as a VPN that applies to the
+     * application. The callbacks will continue to be called until either the application exits or
      * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
      *
      * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
@@ -4203,7 +4209,7 @@
      * {@link #unregisterNetworkCallback(NetworkCallback)}.
      *
      * @param networkCallback The {@link NetworkCallback} that the system will call as the
-     *                        system default network changes.
+     *                        application's default network changes.
      *                        The callback is invoked on the default internal Handler.
      * @throws RuntimeException if the app already has too many callbacks registered.
      */
@@ -4213,10 +4219,46 @@
     }
 
     /**
+     * Registers to receive notifications about changes in the application's default network. This
+     * may be a physical network or a virtual network, such as a VPN that applies to the
+     * application. The callbacks will continue to be called until either the application exits or
+     * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
+     *
+     * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+     * number of outstanding requests to 100 per app (identified by their UID), shared with
+     * all variants of this method, of {@link #requestNetwork} as well as
+     * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+     * Requesting a network with this method will count toward this limit. If this limit is
+     * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+     * make sure to unregister the callbacks with
+     * {@link #unregisterNetworkCallback(NetworkCallback)}.
+     *
+     * @param networkCallback The {@link NetworkCallback} that the system will call as the
+     *                        application's default network changes.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @throws RuntimeException if the app already has too many callbacks registered.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+            @NonNull Handler handler) {
+        CallbackHandler cbHandler = new CallbackHandler(handler);
+        sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
+                TRACK_DEFAULT, TYPE_NONE, cbHandler);
+    }
+
+    /**
      * Registers to receive notifications about changes in the system default network. The callbacks
      * will continue to be called until either the application exits or
      * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
      *
+     * This method should not be used to determine networking state seen by applications, because in
+     * many cases, most or even all application traffic may not use the default network directly,
+     * and traffic from different applications may go on different networks by default. As an
+     * example, if a VPN is connected, traffic from all applications might be sent through the VPN
+     * and not onto the system default network. Applications or system components desiring to do
+     * determine network state as seen by applications should use other methods such as
+     * {@link #registerDefaultNetworkCallback(NetworkCallback, Handler)}.
+     *
      * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
      * number of outstanding requests to 100 per app (identified by their UID), shared with
      * all variants of this method, of {@link #requestNetwork} as well as
@@ -4230,20 +4272,19 @@
      *                        system default network changes.
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      * @throws RuntimeException if the app already has too many callbacks registered.
+     *
+     * @hide
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+    @SystemApi(client = MODULE_LIBRARIES)
+    @SuppressLint({"ExecutorRegistration", "PairedRegistration"})
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
             @NonNull Handler handler) {
-        // This works because if the NetworkCapabilities are null,
-        // ConnectivityService takes them from the default request.
-        //
-        // Since the capabilities are exactly the same as the default request's
-        // capabilities, this request is guaranteed, at all times, to be
-        // satisfied by the same network, if any, that satisfies the default
-        // request, i.e., the system default network.
         CallbackHandler cbHandler = new CallbackHandler(handler);
         sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
-                TRACK_DEFAULT, TYPE_NONE, cbHandler);
+                TRACK_SYSTEM_DEFAULT, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -4845,9 +4886,13 @@
         }
     }
 
-    private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
-        Log.d(TAG, "setOemNetworkPreference called with preference: "
-                + preference.toString());
+    private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+        try {
+            mService.setOemNetworkPreference(preference);
+        } catch (RemoteException e) {
+            Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     @NonNull
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index db8b7ed..f909d13 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
 import android.net.ProxyInfo;
 import android.net.UidRange;
 import android.net.QosSocketInfo;
@@ -65,7 +66,7 @@
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
     NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-            int userId, String callingPackageName);
+            int userId, String callingPackageName, String callingAttributionTag);
 
     boolean isNetworkSupported(int networkType);
 
@@ -74,7 +75,8 @@
     LinkProperties getLinkPropertiesForType(int networkType);
     LinkProperties getLinkProperties(in Network network);
 
-    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
+            String callingAttributionTag);
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     NetworkState[] getAllNetworkState();
@@ -175,10 +177,12 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName);
+            in Messenger messenger, in IBinder binder, String callingPackageName,
+            String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation, String callingPackageName);
+            in PendingIntent operation, String callingPackageName,
+            String callingAttributionTag);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
@@ -240,4 +244,6 @@
 
     void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
     void unregisterQosCallback(in IQosCallback callback);
+
+    void setOemNetworkPreference(in OemNetworkPreferences preference);
 }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 55b2c3c..9d67f0b 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -762,12 +762,14 @@
         final int originalSignalStrength = mSignalStrength;
         final int originalOwnerUid = getOwnerUid();
         final int[] originalAdministratorUids = getAdministratorUids();
+        final TransportInfo originalTransportInfo = getTransportInfo();
         clearAll();
         mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
                 | (1 << TRANSPORT_TEST);
         mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
         mNetworkSpecifier = originalSpecifier;
         mSignalStrength = originalSignalStrength;
+        mTransportInfo = originalTransportInfo;
 
         // Only retain the owner and administrator UIDs if they match the app registering the remote
         // caller that registered the network.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index c4d1b09..b4a651c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -86,17 +104,14 @@
      *       callbacks about the single, highest scoring current network
      *       (if any) that matches the specified NetworkCapabilities, or
      *
-     *     - TRACK_DEFAULT, a hybrid of the two designed such that the
-     *       framework will issue callbacks for the single, highest scoring
-     *       current network (if any) that matches the capabilities of the
-     *       default Internet request (mDefaultRequest), but which cannot cause
-     *       the framework to either create or retain the existence of any
-     *       specific network. Note that from the point of view of the request
-     *       matching code, TRACK_DEFAULT is identical to REQUEST: its special
-     *       behaviour is not due to different semantics, but to the fact that
-     *       the system will only ever create a TRACK_DEFAULT with capabilities
-     *       that are identical to the default request's capabilities, thus
-     *       causing it to share fate in every way with the default request.
+     *     - TRACK_DEFAULT, which causes the framework to issue callbacks for
+     *       the single, highest scoring current network (if any) that will
+     *       be chosen for an app, but which cannot cause the framework to
+     *       either create or retain the existence of any specific network.
+     *
+     *     - TRACK_SYSTEM_DEFAULT, which causes the framework to send callbacks
+     *       for the network (if any) that satisfies the default Internet
+     *       request.
      *
      *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
      *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
@@ -119,6 +134,7 @@
         TRACK_DEFAULT,
         REQUEST,
         BACKGROUND_REQUEST,
+        TRACK_SYSTEM_DEFAULT,
     };
 
     /**
@@ -156,8 +172,30 @@
      * needed in terms of {@link NetworkCapabilities} features
      */
     public static class Builder {
+        /**
+         * Capabilities that are currently compatible with VCN networks.
+         */
+        private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+                NET_CAPABILITY_CAPTIVE_PORTAL,
+                NET_CAPABILITY_DUN,
+                NET_CAPABILITY_FOREGROUND,
+                NET_CAPABILITY_INTERNET,
+                NET_CAPABILITY_NOT_CONGESTED,
+                NET_CAPABILITY_NOT_METERED,
+                NET_CAPABILITY_NOT_RESTRICTED,
+                NET_CAPABILITY_NOT_ROAMING,
+                NET_CAPABILITY_NOT_SUSPENDED,
+                NET_CAPABILITY_NOT_VPN,
+                NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_VALIDATED);
+
         private final NetworkCapabilities mNetworkCapabilities;
 
+        // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+        private boolean mModifiedNotVcnManaged = false;
+
         /**
          * Default constructor for Builder.
          */
@@ -179,6 +217,7 @@
             // maybeMarkCapabilitiesRestricted() doesn't add back.
             final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
             nc.maybeMarkCapabilitiesRestricted();
+            deduceNotVcnManagedCapability(nc);
             return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
                     ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
         }
@@ -195,6 +234,9 @@
          */
         public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -206,6 +248,9 @@
          */
         public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.removeCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -263,6 +308,9 @@
         @NonNull
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
+            // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+            // should not be add back later.
+            mModifiedNotVcnManaged = true;
             return this;
         }
 
@@ -382,6 +430,25 @@
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
         }
+
+        /**
+         * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+         * and user intention, which includes:
+         *   1. For the requests that don't have anything besides
+         *      {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
+         *      allow the callers automatically utilize VCN networks if available.
+         *   2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+         *      do not alter them to allow user fire request that suits their need.
+         *
+         * @hide
+         */
+        private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+            if (mModifiedNotVcnManaged) return;
+            for (final int cap : nc.getCapabilities()) {
+                if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+            }
+            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
     }
 
     // implement the Parcelable interface
@@ -435,25 +502,7 @@
      * @hide
      */
     public boolean isRequest() {
-        return isForegroundRequest() || isBackgroundRequest();
-    }
-
-    /**
-     * Returns true iff. the contained NetworkRequest is one that:
-     *
-     *     - should be associated with at most one satisfying network
-     *       at a time;
-     *
-     *     - should cause a network to be kept up and in the foreground if
-     *       it is the best network which can satisfy the NetworkRequest.
-     *
-     * For full detail of how isRequest() is used for pairing Networks with
-     * NetworkRequests read rematchNetworkAndRequests().
-     *
-     * @hide
-     */
-    public boolean isForegroundRequest() {
-        return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+        return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST;
     }
 
     /**
@@ -550,6 +599,8 @@
                 return NetworkRequestProto.TYPE_REQUEST;
             case BACKGROUND_REQUEST:
                 return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
+            case TRACK_SYSTEM_DEFAULT:
+                return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT;
             default:
                 return NetworkRequestProto.TYPE_UNKNOWN;
         }
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03cfbbb..77c8a4f 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -32,8 +32,6 @@
 import java.net.ProxySelector;
 import java.net.URI;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * A convenience class for accessing the user and default proxy
@@ -66,40 +64,9 @@
     @Deprecated
     public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
 
-    /** @hide */
-    public static final int PROXY_VALID             = 0;
-    /** @hide */
-    public static final int PROXY_HOSTNAME_EMPTY    = 1;
-    /** @hide */
-    public static final int PROXY_HOSTNAME_INVALID  = 2;
-    /** @hide */
-    public static final int PROXY_PORT_EMPTY        = 3;
-    /** @hide */
-    public static final int PROXY_PORT_INVALID      = 4;
-    /** @hide */
-    public static final int PROXY_EXCLLIST_INVALID  = 5;
-
     private static ConnectivityManager sConnectivityManager = null;
 
-    // Hostname / IP REGEX validation
-    // Matches blank input, ips, and domain names
-    private static final String NAME_IP_REGEX =
-        "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
-    private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
-    private static final Pattern HOSTNAME_PATTERN;
-
-    private static final String EXCL_REGEX =
-        "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
-    private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
-    private static final Pattern EXCLLIST_PATTERN;
-
     static {
-        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
-        EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
         sDefaultProxySelector = ProxySelector.getDefault();
     }
 
@@ -218,33 +185,6 @@
         return false;
     }
 
-    /**
-     * Validate syntax of hostname, port and exclusion list entries
-     * {@hide}
-     */
-    public static int validate(String hostname, String port, String exclList) {
-        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
-        Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
-        if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
-        if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
-        if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
-        if (port.length() > 0) {
-            if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
-            int portVal = -1;
-            try {
-                portVal = Integer.parseInt(port);
-            } catch (NumberFormatException ex) {
-                return PROXY_PORT_INVALID;
-            }
-            if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
-        }
-        return PROXY_VALID;
-    }
-
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @Deprecated
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 9c9fed1..229db0d 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.net.module.util.ProxyUtils;
+
 import java.net.InetSocketAddress;
 import java.net.URLConnection;
 import java.util.List;
@@ -233,7 +235,7 @@
      */
     public boolean isValid() {
         if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
-        return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+        return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
                 mPort == 0 ? "" : Integer.toString(mPort),
                 mExclusionList == null ? "" : mExclusionList);
     }
diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
index c87b827..1e30283 100644
--- a/packages/Connectivity/framework/src/android/net/VpnManager.java
+++ b/packages/Connectivity/framework/src/android/net/VpnManager.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,6 +29,8 @@
 import android.content.res.Resources;
 import android.os.RemoteException;
 
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 
 import java.io.IOException;
@@ -52,13 +55,29 @@
 public class VpnManager {
     /** Type representing a lack of VPN @hide */
     public static final int TYPE_VPN_NONE = -1;
-    /** VPN service type code @hide */
+
+    /**
+     * A VPN created by an app using the {@link VpnService} API.
+     * @hide
+     */
     public static final int TYPE_VPN_SERVICE = 1;
-    /** Platform VPN type code @hide */
+
+    /**
+     * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
+     * @hide
+     */
     public static final int TYPE_VPN_PLATFORM = 2;
 
+    /**
+     * An IPsec VPN created by the built-in LegacyVpnRunner.
+     * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead.
+     * @hide
+     */
+    @Deprecated
+    public static final int TYPE_VPN_LEGACY = 3;
+
     /** @hide */
-    @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM})
+    @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
     @Retention(RetentionPolicy.SOURCE)
     public @interface VpnType {}
 
@@ -161,4 +180,104 @@
             throw e.rethrowFromSystemServer();
         }
     }
-}
+
+    /**
+     * Return the VPN configuration for the given user ID.
+     * @hide
+     */
+    @Nullable
+    public VpnConfig getVpnConfig(@UserIdInt int userId) {
+        try {
+            return mService.getVpnConfig(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Prepare for a VPN application.
+     * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+     * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+     *
+     * @param oldPackage Package name of the application which currently controls VPN, which will
+     *                   be replaced. If there is no such application, this should should either be
+     *                   {@code null} or {@link VpnConfig.LEGACY_VPN}.
+     * @param newPackage Package name of the application which should gain control of VPN, or
+     *                   {@code null} to disable.
+     * @param userId User for whom to prepare the new VPN.
+     *
+     * @hide
+     */
+    public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+            int userId) {
+        try {
+            return mService.prepareVpn(oldPackage, newPackage, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+     * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+     * class. If the caller is not {@code userId}, {@link
+     * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+     *
+     * @param packageName The package for which authorization state should change.
+     * @param userId User for whom {@code packageName} is installed.
+     * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+     *     permissions should be granted. When unauthorizing an app, {@link
+     *     VpnManager.TYPE_VPN_NONE} should be used.
+     * @hide
+     */
+    public void setVpnPackageAuthorization(
+            String packageName, int userId, @VpnManager.VpnType int vpnType) {
+        try {
+            mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the legacy VPN information for the specified user ID.
+     * @hide
+     */
+    public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+        try {
+            return mService.getLegacyVpnInfo(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts a legacy VPN.
+     * @hide
+     */
+    public void startLegacyVpn(VpnProfile profile) {
+        try {
+            mService.startLegacyVpn(profile);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+     * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+     * with a reload of its profile.
+     *
+     * <p>This method can only be called by the system UID
+     * @return a boolean indicating success
+     *
+     * @hide
+     */
+    public boolean updateLockdownVpn() {
+        try {
+            return mService.updateLockdownVpn();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 3837743..889980a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string>
     <string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 4ce01d6..c41e4d5 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
     <string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 2580d0f..f8d1d57 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string>
     <string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"‏تم الاتصال تلقائيًا عبر %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"‏تم الاتصال عبر %1$s"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index b61ff50..c6078f8 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d063776..d3e0a25 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string>
@@ -313,7 +312,7 @@
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string>
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string>
-    <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Təkmilləşdirilmiş Bağlantı funksiyasını aktiv edir."</string>
+    <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 2976bb5..8541222 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7af9e93..aab600f 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string>
     <string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 77b6493..92f2935 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string>
     <string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 819625b..b40e24e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 193ac60..cec2e45 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index fef9bfb..6134da8 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 281a788..f29a3dd 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 69cc8d8..d92d41d 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string>
     <string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 737ea16..ac05b620 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string>
     <string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string>
@@ -287,7 +286,7 @@
     <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
     <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string>
     <string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string>
-    <string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string>
+    <string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string>
     <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Größe pro Protokollpuffer wählen"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Speicher der dauerhaften Protokollierung löschen?"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 8e1d5e3..0b11d69 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
     <string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index f79072f..1da8e37 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index f99a3f1..54226ce 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fa2aa46..4c747e3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string>
     <string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2c4a8ee..6016cd2 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1c6ca76..e855570 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -36,10 +36,9 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال به‌صورت خودکار انجام نمی‌شود"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string>
     <string name="saved_network" msgid="7143698034077223645">"ذخیره‌شده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"‏اتصال خودکار ازطریق %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائه‌دهنده رتبه‌بندی شبکه"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائه‌دهنده رده‌بندی شبکه"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"‏متصل از طریق %1$s"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‏در دسترس از طریق %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 04c9130..724e52a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string>
     <string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index aa0cd2a..74c3005 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index dbdc160..c9651ce 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 90c1303..f499f58 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede de pago por consumo"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4caeda2..23ee1de 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 468808b..a2fc43c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 932c256..396051d 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fbaffac..0c9bc54 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string>
     <string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 224d641..56c93b6 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
     <string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 8cf13cd..0440f47 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index aa8893e..0bb294f 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 0199f54..54049d7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index a50a22d..6cc4347 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string>
     <string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"‏מחובר אוטומטית דרך %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"‏מחובר דרך %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index a1d1b70..ec674ad 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 77fd4b1..30ab3b4 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string>
     <string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index eb5cd54..ef78527 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 38abb80..ed59c74 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"មិនមាន​ការតភ្ជាប់​អ៊ីនធឺណិតទេ"</string>
     <string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅ​បណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់​ដោយស្វ័យប្រវត្តិ​តាម​រយៈក្រុមហ៊ុនផ្តល់​ការ​វាយ​តម្លៃលើ​បណ្តាញ"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 560fba15..995dc86 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್‌ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index f43ce16..7863d48 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 35b1ecac..45d05c6 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index f60fe7f..ca6e06c 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
     <string name="saved_network" msgid="7143698034077223645">"ບັນທຶກ​​​ໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"​ເຊື່ອມຕໍ່​ຜ່ານ %1$s ​ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e66e3c5..173b57e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string>
     <string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string>
@@ -208,7 +207,7 @@
     <string name="enable_adb" msgid="8072776357237289039">"USB perkrova"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"Derinimo režimas, kai prijungtas USB"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"Panaikinti USB derinimo prieigos teises"</string>
-    <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derinimas"</string>
+    <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derin."</string>
     <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Derinimo režimas, kai prisijungta prie „Wi‑Fi“"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"Klaida"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Belaidžio ryšio derinimas"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 90bcc04..0ef3f0e 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string>
     <string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index ba1987b..e6289ae 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്‌റ്റുചെയ്യില്ല"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്‌വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്‌റ്റുചെയ്‌തു"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 45a831d..ead712c 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string>
@@ -398,8 +397,8 @@
     <item msgid="1282170165150762976">"Дижитал агуулгад зориулан тааруулсан өнгө"</item>
   </string-array>
     <string name="inactive_apps_title" msgid="5372523625297212320">"Зогсолтын горимын апп"</string>
-    <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string>
-    <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string>
+    <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Асаах/унтраахын тулд дарна уу."</string>
+    <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Асаах/унтраахын тулд дарна уу."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
     <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string>
     <string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1e69b28..edcc71b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अ‍ॅक्सेस नाही"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्‍ट केले"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 71c9eea..8a14437 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3b3983e..377c8b2 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index a8c01b3..f0e773b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string>
     <string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index c285637..0bde70f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 9f4ea68..2bc1e8c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
     <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 31bd7af..787c871 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍‌ର କୌଣସି ଆକ୍‌ସେସ୍‌ ନାହିଁ"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f21c4ce..9483e06 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c9c4a6c..c7f1595 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4b71085..63d8b8f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 4aeb985..ac43e49 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string>
     <string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 525d423..08fd9a0 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්‍රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්‍රවේශය නැත"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්‍රියව සම්බන්ධ විය"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්‍රේණිගත සපයන්නා හරහා ස්වයංක්‍රියව සම්බන්ධ විය"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index ee9ae6a..8f05684 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string>
     <string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ff60a32..3dccf3b 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string>
     <string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 78e6ed6..c09ad4c 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 8bb9277..b5a91bf 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string>
     <string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 03fb223..16a1be6 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string>
     <string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 631a413..58896c8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string>
     <string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5796603..71cf7d4 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 9027ca1..c3a65e7 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్‌వర్క్‌కు కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్‌వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 8358dbb..903accb 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
     <string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index d5250a4..b259201 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 8e0b889..a0bc362 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index c2f71c3..509c8a6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string>
     <string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index a0c5f94..e097ab2 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"‏‎%1$s کے ذریعے از خود منسلک کردہ"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"‏منسلک بذریعہ ‎%1$s"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 1fb610b..8c2a95d 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string>
@@ -156,7 +155,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"Matnni nutqqa aylantirish sozlamalari"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Matnni o‘qish tezligi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 1f3ea48..7a4d89b 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 828d25f..7cd61fc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string>
     <string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 54e1f41..458071c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string>
     <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index b8f1f58..867ed8b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string>
     <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index d680b66..e64dbd3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -36,8 +36,7 @@
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string>
     <string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
-    <skip />
+    <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string>
     <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index b8030f1..4c7b898 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -259,7 +259,12 @@
                 .append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',')
                 .append("dataState=").append(dataState).append(',')
                 .append("serviceState=").append(serviceState == null ? ""
-                        : serviceState.toString()).append(',')
+                        : "mVoiceRegState=" + serviceState.getState() + "("
+                                + ServiceState.rilServiceStateToString(serviceState.getState())
+                                + ")" + ", mDataRegState=" + serviceState.getDataRegState() + "("
+                                + ServiceState.rilServiceStateToString(
+                                        serviceState.getDataRegState()) + ")")
+                                        .append(',')
                 .append("signalStrength=").append(signalStrength == null ? ""
                         : signalStrength.toString()).append(',')
                 .append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 84dacfd..d10ff40 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -251,5 +251,5 @@
     <integer name="def_accessibility_magnification_capabilities">3</integer>
 
     <!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW -->
-    <bool name="def_enable_non_resizable_multi_window">false</bool>
+    <bool name="def_enable_non_resizable_multi_window">true</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ad6a531..66165b6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,10 +147,5 @@
         VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(
-                Global.ONE_HANDED_KEYGUARD_SIDE,
-                new InclusiveIntegerRangeValidator(
-                        /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
-                        /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
 import android.provider.settings.validators.SecureSettingsValidators;
 import android.provider.settings.validators.SystemSettingsValidators;
 import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
@@ -95,10 +96,11 @@
     private static final String KEY_NETWORK_POLICIES = "network_policies";
     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
     private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+    private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 8;
+    private static final int STATE_VERSION = 9;
 
     // Versioning of the Network Policies backup payload.
     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
-    private static final int STATE_SYSTEM           = 0;
-    private static final int STATE_SECURE           = 1;
-    private static final int STATE_LOCALE           = 2;
-    private static final int STATE_WIFI_SUPPLICANT  = 3;
-    private static final int STATE_WIFI_CONFIG      = 4;
-    private static final int STATE_GLOBAL           = 5;
-    private static final int STATE_LOCK_SETTINGS    = 6;
-    private static final int STATE_SOFTAP_CONFIG    = 7;
-    private static final int STATE_NETWORK_POLICIES = 8;
-    private static final int STATE_WIFI_NEW_CONFIG  = 9;
-    private static final int STATE_DEVICE_CONFIG    = 10;
+    private static final int STATE_SYSTEM                = 0;
+    private static final int STATE_SECURE                = 1;
+    private static final int STATE_LOCALE                = 2;
+    private static final int STATE_WIFI_SUPPLICANT       = 3;
+    private static final int STATE_WIFI_CONFIG           = 4;
+    private static final int STATE_GLOBAL                = 5;
+    private static final int STATE_LOCK_SETTINGS         = 6;
+    private static final int STATE_SOFTAP_CONFIG         = 7;
+    private static final int STATE_NETWORK_POLICIES      = 8;
+    private static final int STATE_WIFI_NEW_CONFIG       = 9;
+    private static final int STATE_DEVICE_CONFIG         = 10;
+    private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
 
-    private static final int STATE_SIZE             = 11; // The current number of state items
+    private static final int STATE_SIZE                  = 12; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
             8,              // version 5 added STATE_SOFTAP_CONFIG
             9,              // version 6 added STATE_NETWORK_POLICIES
             10,             // version 7 added STATE_WIFI_NEW_CONFIG
-            STATE_SIZE      // version 8 added STATE_DEVICE_CONFIG
+            11,             // version 8 added STATE_DEVICE_CONFIG
+            STATE_SIZE      // version 9 added STATE_SIM_SPECIFIC_SETTINGS
     };
 
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
@@ -218,6 +222,7 @@
         byte[] netPoliciesData = getNetworkPolicies();
         byte[] wifiFullConfigData = getNewWifiConfigData();
         byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+        byte[] simSpecificSettingsData = getSimSpecificSettingsData();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
@@ -246,6 +251,9 @@
         stateChecksums[STATE_DEVICE_CONFIG] =
                 writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
                         deviceSpecificInformation, data);
+        stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+                writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+                        KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -386,6 +394,12 @@
                             preservedSettings);
                     break;
 
+                case KEY_SIM_SPECIFIC_SETTINGS:
+                    byte[] restoredSimSpecificSettings = new byte[size];
+                    data.readEntityData(restoredSimSpecificSettings, 0, size);
+                    restoreSimSpecificSettings(restoredSimSpecificSettings);
+                    break;
+
                 default :
                     data.skipEntityData();
 
@@ -1189,6 +1203,20 @@
         return true;
     }
 
+    private byte[] getSimSpecificSettingsData() {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+        Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+                + " successfully retrieved");
+
+        return simSpecificData;
+    }
+
+    private void restoreSimSpecificSettings(byte[] data) {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        subManager.restoreAllSimSpecificSettingsFromBackup(data);
+    }
+
     private void updateWindowManagerIfNeeded(Integer previousDensity) {
         int newDensity;
         try {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c7790fd..a1fd7ee 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -297,6 +297,24 @@
         Settings.System.getCloneFromParentOnValueSettings(sSystemCloneFromParentOnDependency);
     }
 
+    private static final Set<String> sAllSecureSettings = new ArraySet<>();
+    private static final Set<String> sReadableSecureSettings = new ArraySet<>();
+    static {
+        Settings.Secure.getPublicSettings(sAllSecureSettings, sReadableSecureSettings);
+    }
+
+    private static final Set<String> sAllSystemSettings = new ArraySet<>();
+    private static final Set<String> sReadableSystemSettings = new ArraySet<>();
+    static {
+        Settings.System.getPublicSettings(sAllSystemSettings, sReadableSystemSettings);
+    }
+
+    private static final Set<String> sAllGlobalSettings = new ArraySet<>();
+    private static final Set<String> sReadableGlobalSettings = new ArraySet<>();
+    static {
+        Settings.Global.getPublicSettings(sAllGlobalSettings, sReadableGlobalSettings);
+    }
+
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -1919,6 +1937,7 @@
         if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
             return;
         }
+        checkReadableAnnotation(settingsType, settingName);
         ApplicationInfo ai = getCallingApplicationInfoOrThrow();
         if (!ai.isInstantApp()) {
             return;
@@ -1932,6 +1951,41 @@
         }
     }
 
+    /**
+     * Check if the target settings key is readable. Reject if the caller app is trying to access a
+     * settings key defined in the Settings.Secure, Settings.System or Settings.Global and is not
+     * annotated as @Readable.
+     * Notice that a key string that is not defined in any of the Settings.* classes will still be
+     * regarded as readable.
+     */
+    private void checkReadableAnnotation(int settingsType, String settingName) {
+        final Set<String> allFields;
+        final Set<String> readableFields;
+        switch (settingsType) {
+            case SETTINGS_TYPE_GLOBAL:
+                allFields = sAllGlobalSettings;
+                readableFields = sReadableGlobalSettings;
+                break;
+            case SETTINGS_TYPE_SYSTEM:
+                allFields = sAllSystemSettings;
+                readableFields = sReadableSystemSettings;
+                break;
+            case SETTINGS_TYPE_SECURE:
+                allFields = sAllSecureSettings;
+                readableFields = sReadableSecureSettings;
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+        }
+
+        if (allFields.contains(settingName) && !readableFields.contains(settingName)) {
+            throw new SecurityException(
+                    "Settings key: <" + settingName + "> is not readable. From S+, new public "
+                            + "settings keys need to be annotated with @Readable unless they are "
+                            + "annotated with @hide.");
+        }
+    }
+
     private ApplicationInfo getCallingApplicationInfoOrThrow() {
         // We always use the callingUid for this lookup. This means that if hypothetically an
         // app was installed in user A with cross user and in user B as an Instant App
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c11877a..438cec8 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,7 +283,6 @@
                     Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
                     Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
                     Settings.Global.FANCY_IME_ANIMATIONS,
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9021864..7bfb42b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -76,8 +76,8 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
-        "kotlinx-coroutines-android",
-        "kotlinx-coroutines-core",
+        "kotlinx_coroutines_android",
+        "kotlinx_coroutines",
         "iconloader_base",
         "SystemUI-tags",
         "SystemUI-proto",
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index b5f55af..7986809 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
     <include
         style="@style/BouncerSecurityContainer"
         layout="@layout/keyguard_host_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
 </FrameLayout>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 35a2195..7a9e7c7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -76,7 +76,6 @@
             android:textSize="100dp"
             android:includeFontPadding="false"
             android:fontFamily="@font/clock"
-            android:lineSpacingMultiplier=".7"
             android:typeface="monospace"
             android:elegantTextHeight="false"
             dozeWeight="200"
@@ -97,7 +96,6 @@
             android:gravity="center_horizontal"
             android:textSize="@dimen/large_clock_text_size"
             android:includeFontPadding="false"
-            android:lineSpacingMultiplier=".7"
             android:fontFamily="@font/clock"
             android:typeface="monospace"
             android:elegantTextHeight="false"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index c75ee51..04e645b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,14 +41,13 @@
         android:layout_gravity="center">
         <com.android.keyguard.KeyguardSecurityViewFlipper
             android:id="@+id/view_flipper"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:paddingTop="@dimen/keyguard_security_view_top_margin"
             android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
             android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
-            android:layout_gravity="center"
             android:gravity="center">
         </com.android.keyguard.KeyguardSecurityViewFlipper>
     </com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c..8d9d6ee 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,4 @@
 
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
-    <bool name="can_use_one_handed_bouncer">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
new file mode 100644
index 0000000..cb4686dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners android:radius="@dimen/notification_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
new file mode 100644
index 0000000..e5389f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
@@ -0,0 +1,44 @@
+<?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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape
+            android:innerRadiusRatio="2.2"
+            android:shape="ring"
+            android:thickness="@dimen/udfps_enroll_progress_thickness"
+            android:useLevel="false"
+            android:tint="?android:colorControlNormal">
+            <solid android:color="@*android:color/white_disabled_material" />
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <rotate
+            android:fromDegrees="270"
+            android:pivotX="50%"
+            android:pivotY="50%"
+            android:toDegrees="270">
+            <shape
+                android:innerRadiusRatio="2.2"
+                android:shape="ring"
+                android:thickness="@dimen/udfps_enroll_progress_thickness"
+                android:tint="?android:attr/colorControlActivated">
+                <solid android:color="@android:color/white" />
+            </shape>
+        </rotate>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml
new file mode 100644
index 0000000..983999f
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_in_dialog.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/control_detail_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
+    android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
+    android:padding="8dp"
+    android:orientation="vertical"
+    android:background="@drawable/controls_dialog_bg">
+
+  <com.android.systemui.globalactions.MinHeightScrollView
+      android:layout_width="match_parent"
+      android:layout_height="0dp"
+      android:layout_weight="1"
+      android:orientation="vertical"
+      android:scrollbars="none">
+
+    <LinearLayout
+        android:id="@+id/global_actions_controls"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:orientation="vertical"
+        android:clipToPadding="false" />
+
+  </com.android.systemui.globalactions.MinHeightScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 04de978..187ae58 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -43,14 +43,17 @@
             android:accessibilityLiveRegion="polite"/>
 
         <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
-            android:id="@+id/keyguard_indication_enterprise_disclosure"
-            android:layout_width="match_parent"
+            android:id="@+id/keyguard_indication_text_bottom"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:gravity="center"
+            android:minHeight="48dp"
+            android:layout_gravity="center_horizontal"
+            android:layout_centerHorizontal="true"
             android:paddingStart="@dimen/keyguard_indication_text_padding"
             android:paddingEnd="@dimen/keyguard_indication_text_padding"
             android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
-            android:alpha=".54"
+            android:alpha=".8"
             android:visibility="gone"/>
 
     </LinearLayout>
@@ -81,6 +84,17 @@
         android:contentDescription="@string/accessibility_phone_button"
         android:tint="?attr/wallpaperTextColor" />
 
+    <ImageView
+        android:id="@+id/alt_left_button"
+        android:layout_height="@dimen/keyguard_affordance_height"
+        android:layout_width="@dimen/keyguard_affordance_width"
+        android:layout_gravity="bottom|start"
+        android:scaleType="center"
+        android:tint="?attr/wallpaperTextColor"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="48dp"
+        android:visibility="gone" />
+
     <FrameLayout
         android:id="@+id/overlay_container"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8f3345f..e2f3e2a 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -74,7 +74,7 @@
         android:id="@+id/preview"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="24dp"
+        android:layout_marginBottom="42dp"
         android:layout_marginHorizontal="48dp"
         android:adjustViewBounds="true"
         app:layout_constrainedHeight="true"
@@ -91,19 +91,33 @@
         android:id="@+id/crop_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="24dp"
+        android:layout_marginBottom="42dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
         app:layout_constraintTop_toBottomOf="@id/guideline"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:handleThickness="3dp"
+        app:handleThickness="@dimen/screenshot_crop_handle_thickness"
         app:handleColor="@*android:color/accent_device_default"
-        app:scrimColor="#9444"
+        app:scrimColor="@color/screenshot_crop_scrim"
         tools:background="?android:colorBackground"
         tools:minHeight="100dp"
         tools:minWidth="100dp" />
 
+    <com.android.systemui.screenshot.MagnifierView
+        android:id="@+id/magnifier"
+        android:visibility="invisible"
+        android:layout_width="200dp"
+        android:layout_height="200dp"
+        app:layout_constraintTop_toBottomOf="@id/guideline"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:handleThickness="@dimen/screenshot_crop_handle_thickness"
+        app:handleColor="@*android:color/accent_device_default"
+        app:scrimColor="@color/screenshot_crop_scrim"
+        app:borderThickness="4dp"
+        app:borderColor="#fff"
+        />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
 
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index c078805..6ae306e 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,4 +20,17 @@
     android:id="@+id/udfps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    systemui:sensorTouchAreaCoefficient="0.5"/>
+    systemui:sensorTouchAreaCoefficient="0.5">
+
+    <!-- Enrollment progress bar-->
+    <com.android.systemui.biometrics.UdfpsProgressBar
+        android:id="@+id/progress_bar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:max="100"
+        android:padding="@dimen/udfps_enroll_progress_thickness"
+        android:progress="0"
+        android:layout_gravity="center"
+        android:visibility="gone"/>
+
+</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6c55fb6..8166e35 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -178,6 +178,14 @@
         <attr name="scrimColor" format="color" />
     </declare-styleable>
 
+    <declare-styleable name="MagnifierView">
+        <attr name="handleThickness" format="dimension" />
+        <attr name="handleColor" format="color" />
+        <attr name="scrimColor" format="color" />
+        <attr name="borderThickness" format="dimension" />
+        <attr name="borderColor" format="color" />
+    </declare-styleable>
+
     <declare-styleable name="RoundedCornerProgressDrawable">
         <attr name="android:drawable" />
     </declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7cf3e9..5fb6de7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -197,6 +197,9 @@
     <color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
     <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
 
+    <!-- Long screenshot UI -->
+    <color name="screenshot_crop_scrim">#9444</color>
+
     <!-- GM2 colors -->
     <color name="GM2_grey_50">#F8F9FA</color>
     <color name="GM2_grey_100">#F1F3F4</color>
@@ -261,6 +264,7 @@
     <color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
     <color name="control_thumbnail_tint">#33000000</color>
     <color name="control_thumbnail_shadow_color">@*android:color/black</color>
+    <color name="controls_lockscreen_scrim">#AA000000</color>
 
     <!-- Docked misalignment message -->
     <color name="misalignment_text_color">#F28B82</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d92f4ea..12b8ccf 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -163,10 +163,14 @@
     <!-- heads up elevation that is added if the view is pinned -->
     <dimen name="heads_up_pinned_elevation">16dp</dimen>
 
-    <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
+    <!-- Height of a messaging notifications with actions at least. Note that this is an upper bound
          and the notification won't use this much, but is measured with wrap_content -->
     <dimen name="notification_messaging_actions_min_height">196dp</dimen>
 
+    <!-- Height of a call notification. Note that this is an upper bound
+     and the notification won't use this much, but is measured with wrap_content -->
+    <dimen name="call_notification_full_height">172dp</dimen>
+
     <!-- a threshold in dp per second that is considered fast scrolling -->
     <dimen name="scroll_fast_threshold">1500dp</dimen>
 
@@ -199,6 +203,9 @@
     <!-- The amount the content shifts upwards when transforming into the shelf -->
     <dimen name="shelf_transform_content_shift">32dp</dimen>
 
+    <!-- The y translation for keyguard indication text animation for rotating text in/out -->
+    <dimen name="keyguard_indication_y_translation">24dp</dimen>
+
     <!-- The padding on the bottom of the notifications on the keyguard -->
     <dimen name="keyguard_indication_bottom_padding">12sp</dimen>
 
@@ -345,6 +352,7 @@
     <dimen name="screenshot_action_chip_padding_end">16dp</dimen>
     <dimen name="screenshot_action_chip_text_size">14sp</dimen>
     <dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+    <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
 
 
     <!-- The width of the view containing navigation buttons -->
@@ -684,6 +692,11 @@
     <!-- The amount to shift the clocks during a small/large transition -->
     <dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
 
+    <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
+    <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
+    <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
+    <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
+
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
 
     <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
@@ -1116,6 +1129,9 @@
     <!-- Y translation for credential contents when animating in -->
     <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
 
+    <!-- UDFPS enrollment progress bar thickness -->
+    <dimen name="udfps_enroll_progress_thickness">12dp</dimen>
+
     <!-- Wireless Charging Animation values -->
     <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
     <dimen name="wireless_charging_dots_radius_end">4dp</dimen>
@@ -1153,7 +1169,7 @@
     <dimen name="logout_button_layout_height">32dp</dimen>
     <dimen name="logout_button_padding_horizontal">16dp</dimen>
     <dimen name="logout_button_margin_bottom">12dp</dimen>
-    <dimen name="logout_button_corner_radius">2dp</dimen>
+    <dimen name="logout_button_corner_radius">4dp</dimen>
 
     <!--  Blur radius on status bar window and power menu  -->
     <dimen name="min_window_blur_radius">1px</dimen>
@@ -1259,6 +1275,7 @@
 
     <!-- Home Controls activity view detail panel-->
     <dimen name="controls_activity_view_top_offset">100dp</dimen>
+    <dimen name="controls_activity_view_side_offset">12dp</dimen>
     <dimen name="controls_activity_view_text_size">17sp</dimen>
     <dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen>
 
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 01e54ff..ded8a2e 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -34,6 +34,9 @@
 
     <bool name="flag_brightness_slider">false</bool>
 
+    <!-- The new animations to/from lockscreen and AOD! -->
+    <bool name="flag_lockscreen_animations">false</bool>
+
     <!-- People Tile flag -->
     <bool name="flag_conversations">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d932395..abcf4e8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -954,11 +954,8 @@
     <string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
     <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
-    <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
-    <!-- QuickSettings: Label for the toggle that controls whether Reduce Bright Colors is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_reduce_bright_colors_label" translatable="false">Reduce Bright Colors</string>
-        <!-- QuickSettings: Secondary text for intensity level of Reduce Bright Colors. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_reduce_bright_colors_secondary_label" translatable="false"> <xliff:g id="intensity" example="50">%d</xliff:g>%% reduction</string>
+    <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
@@ -2256,6 +2253,12 @@
     <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
     <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>
 
+    <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_added">Tile added</string>
+
+    <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] -->
+    <string name="accessibility_qs_edit_tile_removed">Tile removed</string>
+
     <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4b04eeb..7c72548 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -662,6 +662,19 @@
       <item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
     </style>
 
+    <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+      <item name="android:windowAnimationStyle">@style/Animation.Fade</item>
+      <item name="android:windowFullscreen">true</item>
+      <item name="android:windowIsFloating">false</item>
+      <item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
+      <item name="android:backgroundDimEnabled">true</item>
+    </style>
+
+    <style name="Animation.Fade">
+      <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+      <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+    </style>
+
     <style name="Control" />
 
     <style name="Control.MenuItem">
@@ -752,4 +765,12 @@
         <item name="android:textSize">14sp</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
     </style>
+
+    <style name="UdfpsProgressBarStyle"
+        parent="android:style/Widget.Material.ProgressBar.Horizontal">
+        <item name="android:indeterminate">false</item>
+        <item name="android:max">10000</item>
+        <item name="android:mirrorForRtl">false</item>
+        <item name="android:progressDrawable">@drawable/udfps_progress_bar</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e38cf23..5c943f6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -245,9 +245,11 @@
     void setSideStageVisibility(in boolean visible) = 36;
     /** Removes the split-screen stages. */
     void exitSplitScreen() = 37;
-    void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+    /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+    void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
+    void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
     void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
-            in Bundle options, in UserHandle user) = 39;
+            in Bundle options, in UserHandle user) = 40;
     void startIntent(
-            in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
+            in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 19e7d7e..bec9220 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -17,7 +17,6 @@
 package com.android.systemui.shared.system;
 
 import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.view.View;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -37,6 +36,10 @@
             InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
     public static final int CUJ_QUICK_SWITCH =
             InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+    public static final int CUJ_OPEN_ALL_APPS =
+            InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
+    public static final int CUJ_ALL_APPS_SCROLL =
+            InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
 
     @IntDef({
             CUJ_APP_LAUNCH_FROM_RECENTS,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
 
 import android.os.RemoteException;
 import android.util.Log;
@@ -65,13 +67,17 @@
             final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
         return new IRemoteAnimationRunner.Stub() {
             @Override
-            public void onAnimationStart(RemoteAnimationTarget[] apps,
+            public void onAnimationStart(@TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
                     RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
                     final IRemoteAnimationFinishedCallback finishedCallback) {
                 final RemoteAnimationTargetCompat[] appsCompat =
                         RemoteAnimationTargetCompat.wrap(apps);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(wallpapers);
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        RemoteAnimationTargetCompat.wrap(nonApps);
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -83,8 +89,8 @@
                         }
                     }
                 };
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
-                        animationFinishedCallback);
+                remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+                        nonAppsCompat, animationFinishedCallback);
             }
 
             @Override
@@ -104,6 +110,9 @@
                         RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+                // TODO(bc-unlock): Build wrapped object for non-apps target.
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        new RemoteAnimationTargetCompat[0];
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -147,7 +156,10 @@
                     }
                 }
                 t.apply();
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+                // TODO(bc-unlcok): Pass correct transit type.
+                remoteAnimationAdapter.onAnimationStart(
+                        TRANSIT_OLD_NONE,
+                        appsCompat, wallpapersCompat, nonAppsCompat,
                         animationFinishedCallback);
             }
         };
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.shared.system;
 
+import android.view.WindowManager;
+
 public interface RemoteAnimationRunnerCompat {
-    void onAnimationStart(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+    void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+            RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
     void onAnimationCancelled();
 }
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 70021b6..fbabaa4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,10 +114,13 @@
                 for (int i = params.length - 1; i >= 0; i--) {
                     SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
                             params[i];
-                    t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
                     surfaceParams.applyTo(t);
                 }
-                t.apply();
+                if (mTargetViewRootImpl != null) {
+                    mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+                } else {
+                    t.apply();
+                }
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                 Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
                         .sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 4a28d56..89c60f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,4 +56,12 @@
                     });
         }
     }
+
+    public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
+        if (mViewRoot != null) {
+            mViewRoot.mergeWithNextTransaction(t, frame);
+        } else {
+            t.apply();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 59e81cf..0a117c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -16,37 +16,72 @@
 
 package com.android.keyguard;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.icu.text.NumberFormat;
 import android.util.MathUtils;
 
 import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.util.ViewController;
 
+import java.util.Locale;
+import java.util.Objects;
 import java.util.TimeZone;
 
 /**
- * Controls the color of a GradientTextClock.
+ * Controller for an AnimatableClockView.
  */
 public class AnimatableClockController extends ViewController<AnimatableClockView> {
+    private static final int FORMAT_NUMBER = 1234567890;
 
     private final StatusBarStateController mStatusBarStateController;
-    private final int[] mDozingColors = new int[] {Color.WHITE, Color.WHITE};
-    private int[] mLockScreenColors = new int[2];
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final int mDozingColor = Color.WHITE;
+    private int mLockScreenColor;
 
     private boolean mIsDozing;
+    private Locale mLocale;
+
+    private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
+    private final String mBurmeseNumerals;
+    private final float mBurmeseLineSpacing;
+    private final float mDefaultLineSpacing;
 
     public AnimatableClockController(
             AnimatableClockView view,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            BroadcastDispatcher broadcastDispatcher) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mIsDozing = mStatusBarStateController.isDozing();
+        mBroadcastDispatcher = broadcastDispatcher;
+
+        mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
+        mBurmeseLineSpacing = getContext().getResources().getFloat(
+                R.dimen.keyguard_clock_line_spacing_scale_burmese);
+        mDefaultLineSpacing = getContext().getResources().getFloat(
+                R.dimen.keyguard_clock_line_spacing_scale);
     }
 
+    private BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateLocale();
+        }
+    };
+
     @Override
     protected void onViewAttached() {
+        updateLocale();
+        mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mIsDozing = mStatusBarStateController.isDozing();
         refreshTime();
@@ -55,6 +90,7 @@
 
     @Override
     protected void onViewDetached() {
+        mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
     }
 
@@ -84,11 +120,23 @@
         mView.refreshFormat();
     }
 
+    private void updateLocale() {
+        Locale currLocale = Locale.getDefault();
+        if (!Objects.equals(currLocale, mLocale)) {
+            mLocale = currLocale;
+            NumberFormat nf = NumberFormat.getInstance(mLocale);
+            if (nf.format(FORMAT_NUMBER).equals(mBurmeseNumerals)) {
+                mView.setLineSpacingScale(mBurmeseLineSpacing);
+            } else {
+                mView.setLineSpacingScale(mDefaultLineSpacing);
+            }
+        }
+    }
+
     private void initColors() {
-        mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
+        mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
                 com.android.systemui.R.attr.wallpaperTextColor);
-        mLockScreenColors[1] = mLockScreenColors[0]; // same color
-        mView.setColors(mDozingColors, mLockScreenColors);
+        mView.setColors(mDozingColor, mLockScreenColor);
         mView.animateDoze(mIsDozing, false);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ca99563..64b3d73 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -44,12 +44,13 @@
 
     private final Calendar mTime = Calendar.getInstance();
 
-    private CharSequence mFormat;
-    private CharSequence mDescFormat;
-    private int[] mDozingColors;
-    private int[] mLockScreenColors;
     private final int mDozingWeight;
     private final int mLockScreenWeight;
+    private CharSequence mFormat;
+    private CharSequence mDescFormat;
+    private int mDozingColor;
+    private int mLockScreenColor;
+    private float mLineSpacingScale = 1f;
 
     private TextAnimator mTextAnimator = null;
     private Runnable mOnTextAnimatorInitialized;
@@ -111,8 +112,7 @@
                     () -> {
                         invalidate();
                         return Unit.INSTANCE;
-                    },
-                    2 /* number of lines (each can have a unique Paint) */);
+                    });
             if (mOnTextAnimatorInitialized != null) {
                 mOnTextAnimatorInitialized.run();
                 mOnTextAnimatorInitialized = null;
@@ -127,15 +127,20 @@
         mTextAnimator.draw(canvas);
     }
 
-    void setColors(int[] dozingColors, int[] lockScreenColors) {
-        mDozingColors = dozingColors;
-        mLockScreenColors = lockScreenColors;
+    void setLineSpacingScale(float scale) {
+        mLineSpacingScale = scale;
+        setLineSpacing(0, mLineSpacingScale);
+    }
+
+    void setColors(int dozingColor, int lockScreenColor) {
+        mDozingColor = dozingColor;
+        mLockScreenColor = lockScreenColor;
     }
 
     void animateDoze(boolean isDozing, boolean animate) {
         setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
                 -1,
-                isDozing ? mDozingColors : mLockScreenColors,
+                isDozing ? mDozingColor : mLockScreenColor,
                 animate);
     }
 
@@ -152,15 +157,15 @@
     private void setTextStyle(
             @IntRange(from = 0, to = 1000) int weight,
             @FloatRange(from = 0) float textSize,
-            int[] colors,
+            int color,
             boolean animate) {
         if (mTextAnimator != null) {
-            mTextAnimator.setTextStyle(weight, textSize, colors, animate, ANIM_DURATION, null);
+            mTextAnimator.setTextStyle(weight, textSize, color, animate, ANIM_DURATION, null);
         } else {
             // when the text animator is set, update its start values
             mOnTextAnimatorInitialized =
                     () -> mTextAnimator.setTextStyle(
-                            weight, textSize, colors, false, ANIM_DURATION, null);
+                            weight, textSize, color, false, ANIM_DURATION, null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index e0de180..e375877 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -28,6 +28,7 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ClockPlugin;
@@ -56,6 +57,7 @@
     private final ClockManager mClockManager;
     private final KeyguardSliceViewController mKeyguardSliceViewController;
     private final NotificationIconAreaController mNotificationIconAreaController;
+    private final BroadcastDispatcher mBroadcastDispatcher;
 
     /**
      * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
@@ -101,7 +103,8 @@
             SysuiColorExtractor colorExtractor, ClockManager clockManager,
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
-            ContentResolver contentResolver) {
+            ContentResolver contentResolver,
+            BroadcastDispatcher broadcastDispatcher) {
         super(keyguardClockSwitch);
         mResources = resources;
         mStatusBarStateController = statusBarStateController;
@@ -109,6 +112,7 @@
         mClockManager = clockManager;
         mKeyguardSliceViewController = keyguardSliceViewController;
         mNotificationIconAreaController = notificationIconAreaController;
+        mBroadcastDispatcher = broadcastDispatcher;
         mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
     }
 
@@ -231,12 +235,14 @@
                 mNewLockScreenClockViewController =
                         new AnimatableClockController(
                                 mView.findViewById(R.id.animatable_clock_view),
-                                mStatusBarStateController);
+                                mStatusBarStateController,
+                                mBroadcastDispatcher);
                 mNewLockScreenClockViewController.init();
                 mNewLockScreenLargeClockViewController =
                         new AnimatableClockController(
                                 mView.findViewById(R.id.animatable_clock_view_large),
-                                mStatusBarStateController);
+                                mStatusBarStateController,
+                                mBroadcastDispatcher);
                 mNewLockScreenLargeClockViewController.init();
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
 
         @Override
         public void onDisplayChanged(int displayId) {
-            if (displayId == DEFAULT_DISPLAY) return;
-            final Presentation presentation = mPresentations.get(displayId);
-            if (presentation != null && mShowing) {
-                hidePresentation(displayId);
-                // update DisplayInfo.
-                final Display display = mDisplayService.getDisplay(displayId);
-                if (display != null) {
-                    showPresentation(display);
-                }
-            }
+
         }
 
         @Override
@@ -305,15 +296,16 @@
         }
 
         @Override
+        public void onDisplayChanged() {
+            updateBounds();
+            getWindow().getDecorView().requestLayout();
+        }
+
+        @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
 
-            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
-                    .getBounds();
-            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
-            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
-            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
-            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+            updateBounds();
 
             setContentView(LayoutInflater.from(mContext)
                     .inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
 
             mKeyguardClockSwitchController.init();
         }
+
+        private void updateBounds() {
+            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+                    .getBounds();
+            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c182fd1..5f6fd30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,17 +29,12 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.OrientationEventListener;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimationControlListener;
@@ -60,7 +55,6 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.List;
 
@@ -105,12 +99,6 @@
     private boolean mDisappearAnimRunning;
     private SwipeListener mSwipeListener;
 
-    private boolean mIsSecurityViewLeftAligned = true;
-    private boolean mOneHandedMode = false;
-    private SecurityMode mSecurityMode = SecurityMode.Invalid;
-    private ViewPropertyAnimator mRunningOneHandedAnimator;
-    private final OrientationEventListener mOrientationEventListener;
-
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
             new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
 
@@ -169,20 +157,16 @@
     // Used to notify the container when something interesting happens.
     public interface SecurityCallback {
         boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
-
         void userActivity();
-
         void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
 
         /**
-         * @param strongAuth   wheher the user has authenticated with strong authentication like
-         *                     pattern, password or PIN but not by trust agents or fingerprint
+         * @param strongAuth wheher the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
          * @param targetUserId a user that needs to be the foreground user at the finish completion.
          */
         void finish(boolean strongAuth, int targetUserId);
-
         void reset();
-
         void onCancelClicked();
     }
 
@@ -240,136 +224,12 @@
         super(context, attrs, defStyle);
         mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
         mViewConfiguration = ViewConfiguration.get(context);
-
-        mOrientationEventListener = new OrientationEventListener(context) {
-            @Override
-            public void onOrientationChanged(int orientation) {
-                updateLayoutForSecurityMode(mSecurityMode);
-            }
-        };
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
-        mSecurityMode = securityMode;
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
         updateBiometricRetry(securityMode, faceAuthEnabled);
 
-        updateLayoutForSecurityMode(securityMode);
-        mOrientationEventListener.enable();
-    }
-
-    void updateLayoutForSecurityMode(SecurityMode securityMode) {
-        mSecurityMode = securityMode;
-        mOneHandedMode = canUseOneHandedBouncer();
-
-        if (mOneHandedMode) {
-            mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
-        }
-
-        updateSecurityViewGravity();
-        updateSecurityViewLocation(false);
-    }
-
-    /** Return whether the one-handed keyguard should be enabled. */
-    private boolean canUseOneHandedBouncer() {
-        // Is it enabled?
-        if (!getResources().getBoolean(
-                com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
-            return false;
-        }
-
-        if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
-            return false;
-        }
-
-        return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
-    }
-
-    /** Read whether the one-handed keyguard should be on the left/right from settings. */
-    private boolean isOneHandedKeyguardLeftAligned(Context context) {
-        try {
-            return Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
-                    == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
-        } catch (Settings.SettingNotFoundException ex) {
-            return true;
-        }
-    }
-
-    private void updateSecurityViewGravity() {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
-
-        if (mOneHandedMode) {
-            lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
-        } else {
-            lp.gravity = Gravity.CENTER;
-        }
-
-        securityView.setLayoutParams(lp);
-    }
-
-    /**
-     * Moves the inner security view to the correct location (in one handed mode) with animation.
-     * This is triggered when the user taps on the side of the screen that is not currently occupied
-     * by the security view .
-     */
-    private void updateSecurityViewLocation(boolean animate) {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        if (!mOneHandedMode) {
-            securityView.setTranslationX(0);
-            return;
-        }
-
-        if (mRunningOneHandedAnimator != null) {
-            mRunningOneHandedAnimator.cancel();
-            mRunningOneHandedAnimator = null;
-        }
-
-        int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
-
-        if (animate) {
-            mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
-            mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mRunningOneHandedAnimator = null;
-                }
-            });
-
-            mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mRunningOneHandedAnimator.start();
-        } else {
-            securityView.setTranslationX(targetTranslation);
-        }
-    }
-
-    @Nullable
-    private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-
-            if (isKeyguardSecurityView(child)) {
-                return (KeyguardSecurityViewFlipper) child;
-            }
-        }
-
-        return null;
-    }
-
-    private boolean isKeyguardSecurityView(View view) {
-        return view instanceof KeyguardSecurityViewFlipper;
     }
 
     public void onPause() {
@@ -378,7 +238,6 @@
             mAlertDialog = null;
         }
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
-        mOrientationEventListener.disable();
     }
 
     @Override
@@ -460,44 +319,19 @@
                 if (mSwipeListener != null) {
                     mSwipeListener.onSwipeUp();
                 }
-            } else {
-                if (!mIsDragging) {
-                    handleTap(event);
-                }
             }
         }
         return true;
     }
 
-    private void handleTap(MotionEvent event) {
-        // If we're using a fullscreen security mode, skip
-        if (!mOneHandedMode) {
-            return;
-        }
-
-        // Did the tap hit the "other" side of the bouncer?
-        if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
-                || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
-            mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
-            Settings.Global.putInt(
-                    mContext.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
-                    mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
-                            : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
-            updateSecurityViewLocation(true);
-        }
-    }
-
     void setSwipeListener(SwipeListener swipeListener) {
         mSwipeListener = swipeListener;
     }
 
     private void startSpringAnimation(float startVelocity) {
         mSpringAnimation
-                .setStartVelocity(startVelocity)
-                .animateToFinalPosition(0);
+            .setStartVelocity(startVelocity)
+            .animateToFinalPosition(0);
     }
 
     public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -607,17 +441,18 @@
         return insets.inset(0, 0, 0, inset);
     }
 
+
     private void showDialog(String title, String message) {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
         }
 
         mAlertDialog = new AlertDialog.Builder(mContext)
-                .setTitle(title)
-                .setMessage(message)
-                .setCancelable(false)
-                .setNeutralButton(R.string.ok, null)
-                .create();
+            .setTitle(title)
+            .setMessage(message)
+            .setCancelable(false)
+            .setNeutralButton(R.string.ok, null)
+            .create();
         if (!(mContext instanceof Activity)) {
             mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         }
@@ -655,44 +490,6 @@
         }
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
-
-        int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(widthMeasureSpec) / 2,
-                MeasureSpec.getMode(widthMeasureSpec));
-
-        for (int i = 0; i < getChildCount(); i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() != GONE) {
-                if (mOneHandedMode && isKeyguardSecurityView(view)) {
-                    measureChildWithMargins(view, halfWidthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                } else {
-                    measureChildWithMargins(view, widthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                }
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                maxWidth = Math.max(maxWidth,
-                        view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-                maxHeight = Math.max(maxHeight,
-                        view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-                childState = combineMeasuredStates(childState, view.getMeasuredState());
-            }
-        }
-
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
-        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
     void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
         String message = null;
         switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..1a8d420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,7 +404,6 @@
         if (newView != null) {
             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
             mSecurityViewFlipperController.show(newView);
-            mView.updateLayoutForSecurityMode(securityMode);
         }
 
         mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 631c248..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,13 +92,4 @@
                 throw new IllegalStateException("Unknown security quality:" + security);
         }
     }
-
-    /**
-     * Returns whether the given security view should be used in a "one handed" way. This can be
-     * used to change how the security view is drawn (e.g. take up less of the screen, and align to
-     * one side).
-     */
-    public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
-        return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 97aa26f..fea152a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -56,8 +56,10 @@
     private final IActivityManager mIActivityManager;
 
     private TextView mLogoutView;
+    private boolean mCanShowLogout = true; // by default, try to show the logout button here
     private KeyguardClockSwitch mClockView;
     private TextView mOwnerInfo;
+    private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here
     private KeyguardSliceView mKeyguardSlice;
     private View mNotificationIcons;
     private Runnable mPendingMarqueeStart;
@@ -114,6 +116,25 @@
         if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
     }
 
+    void setCanShowOwnerInfo(boolean canShowOwnerInfo) {
+        mCanShowOwnerInfo = canShowOwnerInfo;
+        mOwnerInfo = findViewById(R.id.owner_info);
+        if (mOwnerInfo != null) {
+            if (mCanShowOwnerInfo) {
+                mOwnerInfo.setVisibility(VISIBLE);
+                updateOwnerInfo();
+            } else {
+                mOwnerInfo.setVisibility(GONE);
+                mOwnerInfo = null;
+            }
+        }
+    }
+
+    void setCanShowLogout(boolean canShowLogout) {
+        mCanShowLogout = canShowLogout;
+        updateLogoutView();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -128,7 +149,10 @@
         if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
             mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
         }
-        mOwnerInfo = findViewById(R.id.owner_info);
+        if (mCanShowOwnerInfo) {
+            mOwnerInfo = findViewById(R.id.owner_info);
+        }
+
         mKeyguardSlice = findViewById(R.id.keyguard_status_area);
         mTextColor = mClockView.getCurrentTextColor();
 
@@ -189,7 +213,7 @@
         if (mLogoutView == null) {
             return;
         }
-        mLogoutView.setVisibility(shouldShowLogout() ? VISIBLE : GONE);
+        mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE);
         // Logout button will stay in language of user 0 if we don't set that manually.
         mLogoutView.setText(mContext.getResources().getString(
                 com.android.internal.R.string.global_action_logout));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 973b493..a5f364d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -134,7 +134,7 @@
     }
 
     /**
-     * Get the height of the logout button.
+     * Get the height of the owner information view.
      */
     public int getOwnerInfoHeight() {
         return mView.getOwnerInfoHeight();
@@ -335,9 +335,13 @@
                 // of the top of the view
                 mKeyguardSliceViewController.updateTopMargin(
                         mKeyguardClockSwitchController.getClockTextTopPadding());
+                mView.setCanShowOwnerInfo(false);
+                mView.setCanShowLogout(false);
             } else {
                 // reset margin
                 mKeyguardSliceViewController.updateTopMargin(0);
+                mView.setCanShowOwnerInfo(true);
+                mView.setCanShowLogout(false);
             }
             updateAodIcons();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
index f2d36d1..5735a4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -54,11 +54,10 @@
  */
 class TextAnimator(
     layout: Layout,
-    private val invalidateCallback: () -> Unit,
-    private val numLines: Int = 1
+    private val invalidateCallback: () -> Unit
 ) {
     // Following two members are for mutable for testing purposes.
-    internal var textInterpolator: TextInterpolator = TextInterpolator(layout, numLines)
+    internal var textInterpolator: TextInterpolator = TextInterpolator(layout)
     internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
         duration = DEFAULT_ANIMATION_DURATION
         addUpdateListener {
@@ -99,7 +98,7 @@
     fun setTextStyle(
         weight: Int = -1,
         textSize: Float = -1f,
-        colors: IntArray? = null,
+        color: Int? = null,
         animate: Boolean = true,
         duration: Long = -1L,
         interpolator: TimeInterpolator? = null
@@ -110,21 +109,13 @@
         }
 
         if (textSize >= 0) {
-            for (targetPaint in textInterpolator.targetPaint)
-                targetPaint.textSize = textSize
+            textInterpolator.targetPaint.textSize = textSize
         }
         if (weight >= 0) {
-            for (targetPaint in textInterpolator.targetPaint)
-                targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+            textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
         }
-        if (colors != null) {
-            require(colors.size == textInterpolator.targetPaint.size) {
-                "colors size (${colors.size}) must be the same size as" +
-                    " targetPaints size (${textInterpolator.targetPaint.size})," +
-                    " which was initialized as numLines ($numLines)"
-            }
-            for ((index, targetPaint) in textInterpolator.targetPaint.withIndex())
-                targetPaint.color = colors[index]
+        if (color != null) {
+            textInterpolator.targetPaint.color = color
         }
         textInterpolator.onTargetPaintModified()
 
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
index 0d41a2f..5d5797c 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -30,8 +30,7 @@
  * Provide text style linear interpolation for plain text.
  */
 class TextInterpolator(
-    layout: Layout,
-    lines: Int = 1
+    layout: Layout
 ) {
 
     /**
@@ -40,11 +39,9 @@
      * Once you modified the style parameters, you have to call reshapeText to recalculate base text
      * layout.
      *
-     * @return an array list of paint objects representing one paint per line of text. If this
-     * list has a smaller size than the number of lines, all extra lines will use the Paint an
-     * index 0.
+     * @return a paint object
      */
-    val basePaint = createDefaultPaint(layout.paint, lines)
+    val basePaint = TextPaint(layout.paint)
 
     /**
      * Returns target paint used for interpolation.
@@ -52,18 +49,9 @@
      * Once you modified the style parameters, you have to call reshapeText to recalculate target
      * text layout.
      *
-     * @return an array list of paint objects representing one paint per line of text. If this
-     * list has a smaller size than the number of lines, all extra lines will use the Paint an
-     * index 0.
+     * @return a paint object
      */
-    val targetPaint = createDefaultPaint(layout.paint, lines)
-
-    private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> {
-        val paintList = ArrayList<TextPaint>()
-        for (i in 0 until lines)
-            paintList.add(TextPaint(paint))
-        return paintList
-    }
+    val targetPaint = TextPaint(layout.paint)
 
     /**
      * A class represents a single font run.
@@ -102,7 +90,7 @@
     private val fontInterpolator = FontInterpolator()
 
     // Recycling object for glyph drawing. Will be extended for the longest font run if needed.
-    private val tmpDrawPaints = ArrayList<TextPaint>()
+    private val tmpDrawPaint = TextPaint()
     private var tmpPositionArray = FloatArray(20)
 
     /**
@@ -216,10 +204,10 @@
         if (progress == 0f) {
             return
         } else if (progress == 1f) {
-            updatePaint(basePaint, targetPaint)
+            basePaint.set(targetPaint)
         } else {
-            lerp(basePaint, targetPaint, progress, tmpDrawPaints)
-            updatePaint(basePaint, tmpDrawPaints)
+            lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+            basePaint.set(tmpDrawPaint)
         }
 
         lines.forEach { line ->
@@ -237,21 +225,13 @@
         progress = 0f
     }
 
-    companion object {
-        fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) {
-            toUpdate.clear()
-            for (paint in newValues)
-                toUpdate.add(TextPaint(paint))
-        }
-    }
-
     /**
      * Draws interpolated text at the given progress.
      *
      * @param canvas a canvas.
      */
     fun draw(canvas: Canvas) {
-        lerp(basePaint, targetPaint, progress, tmpDrawPaints)
+        lerp(basePaint, targetPaint, progress, tmpDrawPaint)
         lines.forEachIndexed { lineNo, line ->
             line.runs.forEach { run ->
                 canvas.save()
@@ -261,10 +241,7 @@
                     canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
 
                     run.fontRuns.forEach { fontRun ->
-                        if (lineNo >= tmpDrawPaints.size)
-                            drawFontRun(canvas, run, fontRun, tmpDrawPaints[0])
-                        else
-                            drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo])
+                        drawFontRun(canvas, run, fontRun, tmpDrawPaint)
                     }
                 } finally {
                     canvas.restore()
@@ -430,27 +407,19 @@
     }
 
     // Linear interpolate the paint.
-    private fun lerp(
-        from: ArrayList<TextPaint>,
-        to: ArrayList<TextPaint>,
-        progress: Float,
-        out: ArrayList<TextPaint>
-    ) {
-        out.clear()
+    private fun lerp(from: Paint, to: Paint, progress: Float, out: Paint) {
+        out.set(from)
+
         // Currently only font size & colors are interpolated.
         // TODO(172943390): Add other interpolation or support custom interpolator.
-        for (index in from.indices) {
-            val paint = TextPaint(from[index])
-            paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress)
-            paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress)
-            out.add(paint)
-        }
+        out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
+        out.color = ColorUtils.blendARGB(from.color, to.color, progress)
     }
 
     // Shape the text and stores the result to out argument.
     private fun shapeText(
         layout: Layout,
-        paints: ArrayList<TextPaint>
+        paint: TextPaint
     ): List<List<PositionedGlyphs>> {
         val out = mutableListOf<List<PositionedGlyphs>>()
         for (lineNo in 0 until layout.lineCount) { // Shape all lines.
@@ -458,7 +427,7 @@
             val count = layout.getLineEnd(lineNo) - lineStart
             val runs = mutableListOf<PositionedGlyphs>()
             TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
-                    paints[lineNo]) { _, _, glyphs, _ ->
+                    paint) { _, _, glyphs, _ ->
                 runs.add(glyphs)
             }
             out.add(runs)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 055270d..7d06dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -137,7 +136,8 @@
                         mActivityTaskManager.getTasks(1);
                 if (!runningTasks.isEmpty()) {
                     final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                    if (!topPackage.contentEquals(clientPackage)) {
+                    if (!topPackage.contentEquals(clientPackage)
+                            && !Utils.isSystem(mContext, clientPackage)) {
                         Log.w(TAG, "Evicting client due to: " + topPackage);
                         mCurrentDialog.dismissWithoutCallback(true /* animate */);
                         mCurrentDialog = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index e07c8403..5290986 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -38,6 +38,7 @@
     private static final String TAG = "UdfpsAnimationEnroll";
 
     private static final float SHADOW_RADIUS = 5.f;
+    private static final float PROGRESS_BAR_RADIUS = 140.f;
 
     @Nullable private RectF mSensorRect;
     @NonNull private final Paint mSensorPaint;
@@ -81,12 +82,12 @@
 
     @Override
     public int getPaddingX() {
-        return (int) Math.ceil(SHADOW_RADIUS);
+        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
     }
 
     @Override
     public int getPaddingY() {
-        return (int) Math.ceil(SHADOW_RADIUS);
+        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
     }
 
     @Override
@@ -104,12 +105,4 @@
     public int getOpacity() {
         return 0;
     }
-
-    public void onEnrollmentProgress(int remaining) {
-        Log.d(TAG, "Remaining: " + remaining);
-    }
-
-    public void onEnrollmentHelp() {
-        Log.d(TAG, "onEnrollmentHelp");
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 4e3419e..41ea4d6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -103,18 +103,6 @@
         postInvalidate();
     }
 
-    void onEnrollmentProgress(int remaining) {
-        if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
-            ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
-        }
-    }
-
-    void onEnrollmentHelp() {
-        if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
-            ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
-        }
-    }
-
     public int getPaddingX() {
         if (mUdfpsAnimation == null) {
             return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index baa5973..e7b08e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -84,6 +84,7 @@
     private boolean mIsOverlayRequested;
     // Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
     private int mRequestReason;
+    @Nullable UdfpsEnrollHelper mEnrollHelper;
 
     // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
     // to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -95,6 +96,12 @@
     public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
         @Override
         public void showUdfpsOverlay(int sensorId, int reason) {
+            if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
+                    || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+                mEnrollHelper = new UdfpsEnrollHelper(reason);
+            } else {
+                mEnrollHelper = null;
+            }
             UdfpsController.this.showOverlay(reason);
         }
 
@@ -260,15 +267,17 @@
         // Transform dimensions if the device is in landscape mode.
         switch (mContext.getDisplay().getRotation()) {
             case Surface.ROTATION_90:
-                mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
-                mCoreLayoutParams.y =
-                        p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+                mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                        - paddingX;
+                mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                        - paddingY;
                 break;
 
             case Surface.ROTATION_270:
-                mCoreLayoutParams.x =
-                        p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
-                mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+                mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+                        - paddingX;
+                mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+                        - paddingY;
                 break;
 
             default:
@@ -294,7 +303,7 @@
                 try {
                     Log.v(TAG, "showUdfpsOverlay | adding window");
                     final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
-                    mView.setUdfpsAnimation(animation);
+                    mView.setExtras(animation, mEnrollHelper);
                     mWindowManager.addView(mView, computeLayoutParams(animation));
                     mView.setOnTouchListener(mOnTouchListener);
                     mIsOverlayShowing = true;
@@ -311,7 +320,8 @@
     private UdfpsAnimation getUdfpsAnimationForReason(int reason) {
         Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
         switch (reason) {
-            case IUdfpsOverlayController.REASON_ENROLL:
+            case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
+            case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
                 return new UdfpsAnimationEnroll(mContext);
             case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
                 return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController);
@@ -327,7 +337,7 @@
         mFgExecutor.execute(() -> {
             if (mIsOverlayShowing) {
                 Log.v(TAG, "hideUdfpsOverlay | removing window");
-                mView.setUdfpsAnimation(null);
+                mView.setExtras(null, null);
                 mView.setOnTouchListener(null);
                 // Reset the controller back to its starting state.
                 onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
new file mode 100644
index 0000000..2442633
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.biometrics;
+
+import android.hardware.fingerprint.IUdfpsOverlayController;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Helps keep track of enrollment state and animates the progress bar accordingly.
+ */
+public class UdfpsEnrollHelper {
+    private static final String TAG = "UdfpsEnrollHelper";
+
+    // IUdfpsOverlayController reason
+    private final int mEnrollReason;
+
+    private int mTotalSteps = -1;
+    private int mCurrentProgress = 0;
+
+    public UdfpsEnrollHelper(int reason) {
+        mEnrollReason = reason;
+    }
+
+    boolean shouldShowProgressBar() {
+        return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+    }
+
+    void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+        if (mTotalSteps == -1) {
+            mTotalSteps = remaining;
+        }
+
+        mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
+                / (mTotalSteps + 1);
+        progressBar.setProgress(mCurrentProgress, true /* animate */);
+    }
+
+    void updateProgress(@NonNull UdfpsProgressBar progressBar) {
+        progressBar.setProgress(mCurrentProgress);
+    }
+
+    void onEnrollmentHelp() {
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
new file mode 100644
index 0000000..84e2fab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
@@ -0,0 +1,59 @@
+/*
+ * 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.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ProgressBar;
+
+import com.android.systemui.R;
+
+/**
+ * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting
+ * from the 12 o'clock position. This view maintain equal width and height using a strategy similar
+ * to "centerInside" for ImageView.
+ */
+public class UdfpsProgressBar extends ProgressBar {
+
+    public UdfpsProgressBar(Context context) {
+        this(context, null);
+    }
+
+    public UdfpsProgressBar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle);
+    }
+
+    public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        final int measuredHeight = getMeasuredHeight();
+        final int measuredWidth = getMeasuredWidth();
+
+        final int length = Math.min(measuredHeight, measuredWidth);
+        setMeasuredDimension(length, length);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 7e378d3..00cb28b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -56,6 +56,8 @@
     @NonNull private final RectF mSensorRect;
     @NonNull private final Paint mDebugTextPaint;
 
+    @Nullable private UdfpsProgressBar mProgressBar;
+
     // Used to obtain the sensor location.
     @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
 
@@ -64,6 +66,7 @@
     private boolean mIlluminationRequested;
     private int mStatusBarState;
     private boolean mNotificationShadeExpanded;
+    @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
     public UdfpsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -108,8 +111,17 @@
         mSensorProps = properties;
     }
 
-    void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
+    void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
         mAnimationView.setAnimation(animation);
+        mEnrollHelper = enrollHelper;
+
+        if (enrollHelper != null) {
+            mEnrollHelper.updateProgress(mProgressBar);
+            mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar()
+                    ? View.VISIBLE : View.GONE);
+        } else {
+            mProgressBar.setVisibility(View.GONE);
+        }
     }
 
     @Override
@@ -138,6 +150,11 @@
     }
 
     @Override
+    protected void onFinishInflate() {
+        mProgressBar = findViewById(R.id.progress_bar);
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         mSensorRect.set(0 + mAnimationView.getPaddingX(),
@@ -233,10 +250,10 @@
     }
 
     void onEnrollmentProgress(int remaining) {
-        mAnimationView.onEnrollmentProgress(remaining);
+        mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
     }
 
     void onEnrollmentHelp() {
-        mAnimationView.onEnrollmentHelp();
+
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index fd5e85a..076c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,13 +16,16 @@
 
 package com.android.systemui.biometrics;
 
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.os.UserManager;
@@ -116,4 +119,10 @@
 
         return false;
     }
+
+    static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+        final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+                == PackageManager.PERMISSION_GRANTED;
+        return hasPermission && "android".equals(clientPackage);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 429f67f..d06568a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -24,6 +24,9 @@
  */
 interface ControlActionCoordinator {
 
+    // Handle actions launched from GlobalActionsDialog or ControlDialog
+    var startedFromGlobalActions: Boolean
+
     /**
      * Close any dialogs which may have been open
      */
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 7cd7e18..247f25e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -30,6 +30,7 @@
 import android.service.controls.actions.FloatAction
 import android.util.Log
 import android.view.HapticFeedbackConstants
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.globalactions.GlobalActionsComponent
@@ -37,6 +38,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.wm.shell.TaskViewFactory
+import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
 
@@ -48,13 +50,17 @@
     private val activityStarter: ActivityStarter,
     private val keyguardStateController: KeyguardStateController,
     private val globalActionsComponent: GlobalActionsComponent,
-    private val taskViewFactory: Optional<TaskViewFactory>
+    private val taskViewFactory: Optional<TaskViewFactory>,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val lazyUiController: Lazy<ControlsUiController>
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
     private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
     private var pendingAction: Action? = null
     private var actionsInProgress = mutableSetOf<String>()
 
+    override var startedFromGlobalActions: Boolean = true
+
     companion object {
         private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
     }
@@ -131,8 +137,8 @@
 
     private fun bouncerOrRun(action: Action) {
         if (keyguardStateController.isShowing()) {
-            var closeGlobalActions = !keyguardStateController.isUnlocked()
-            if (closeGlobalActions) {
+            var closeDialog = !keyguardStateController.isUnlocked()
+            if (closeDialog) {
                 context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
 
                 // pending actions will only run after the control state has been refreshed
@@ -141,8 +147,12 @@
 
             activityStarter.dismissKeyguardThenExecute({
                 Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
-                if (closeGlobalActions) {
-                    globalActionsComponent.handleShowGlobalActionsMenu()
+                if (closeDialog) {
+                    if (startedFromGlobalActions) {
+                        globalActionsComponent.handleShowGlobalActionsMenu()
+                    } else {
+                        ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
+                    }
                 } else {
                     action.invoke()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
new file mode 100644
index 0000000..f533cfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.systemui.controls.ui
+
+import android.app.Dialog
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+
+/**
+ * Show the controls space inside a dialog, as from the lock screen.
+ */
+class ControlsDialog(
+    thisContext: Context,
+    val broadcastDispatcher: BroadcastDispatcher
+) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
+
+    private val receiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            val action = intent.getAction()
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+                dismiss()
+            }
+        }
+    }
+
+    init {
+        window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+
+        setContentView(R.layout.controls_in_dialog)
+
+        requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+            setOnClickListener { dismiss() }
+            (getParent() as View).setOnClickListener { dismiss() }
+        }
+    }
+
+    fun show(
+        controller: ControlsUiController
+    ): ControlsDialog {
+        super.show()
+
+        val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
+        vg.alpha = 0f
+        controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */)
+
+        vg.animate()
+            .alpha(1f)
+            .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+            .setDuration(300)
+
+        val filter = IntentFilter()
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+        broadcastDispatcher.registerReceiver(receiver, filter)
+
+        return this
+    }
+
+    override fun dismiss() {
+        broadcastDispatcher.unregisterReceiver(receiver)
+
+        if (!isShowing()) return
+
+        super.dismiss()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 4e4c82c..9448877 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -29,7 +29,7 @@
         public const val EXTRA_ANIMATE = "extra_animate"
     }
 
-    fun show(parent: ViewGroup, dismissGlobalActions: Runnable)
+    fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
     fun hide()
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 2b529f9..762362c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -102,7 +102,7 @@
     private lateinit var lastItems: List<SelectionItem>
     private var popup: ListPopupWindow? = null
     private var hidden = true
-    private lateinit var dismissGlobalActions: Runnable
+    private lateinit var onDismiss: Runnable
     private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
     private var retainCache = false
 
@@ -145,13 +145,19 @@
         }
     }
 
-    override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) {
+    override fun show(
+        parent: ViewGroup,
+        onDismiss: Runnable,
+        startedFromGlobalActions: Boolean
+    ) {
         Log.d(ControlsUiController.TAG, "show()")
         this.parent = parent
-        this.dismissGlobalActions = dismissGlobalActions
+        this.onDismiss = onDismiss
         hidden = false
         retainCache = false
 
+        controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
+
         allStructures = controlsController.get().getFavorites()
         selectedStructure = loadPreference(allStructures)
 
@@ -187,7 +193,7 @@
                 controlViewsById.clear()
                 controlsById.clear()
 
-                show(parent, dismissGlobalActions)
+                show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
                 val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
                 showAnim.setInterpolator(DecelerateInterpolator(1.0f))
                 showAnim.setDuration(FADE_IN_MILLIS)
@@ -260,7 +266,7 @@
     private fun startActivity(context: Context, intent: Intent) {
         // Force animations when transitioning from a dialog to an activity
         intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
-        dismissGlobalActions.run()
+        onDismiss.run()
 
         activityStarter.dismissKeyguardThenExecute({
             shadeController.collapsePanel(false)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index ec4a91c..2b362b9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.ForegroundServicesDialog;
 import com.android.systemui.keyguard.WorkLockActivity;
+import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
@@ -92,4 +93,10 @@
     @IntoMap
     @ClassKey(TvNotificationPanelActivity.class)
     public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
+
+    /** Inject into PeopleSpaceActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(PeopleSpaceActivity.class)
+    public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 062410a..ffb8446 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -33,7 +33,7 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.util.Optional;
 
@@ -86,7 +86,7 @@
         Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
 
         @BindsInstance
-        Builder setTransitions(Transitions t);
+        Builder setTransitions(RemoteTransitions t);
 
         SysUIComponent build();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 60b665f..84dd259 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -27,7 +27,7 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.util.Optional;
 
@@ -55,16 +55,12 @@
         getShellInit().init();
     }
 
-    // Gets the Shell init instance
     @WMSingleton
     ShellInit getShellInit();
 
-    // Gets the Shell dump instance
     @WMSingleton
     Optional<ShellCommandHandler> getShellCommandHandler();
 
-    // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
-    //                    depending on the threading mechanism we go with
     @WMSingleton
     Optional<OneHanded> getOneHanded();
 
@@ -89,7 +85,6 @@
     @WMSingleton
     Optional<TaskViewFactory> getTaskViewFactory();
 
-    /** Gets transitions */
     @WMSingleton
-    Transitions getTransitions();
+    RemoteTransitions getTransitions();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ad4c447..8af45a5 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2229,7 +2229,8 @@
 
         private void showControls(ControlsUiController controller) {
             mControlsUiController = controller;
-            mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+            mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+                    true /* startedFromGlobalActions */);
         }
 
         private boolean isWalletViewAvailable() {
@@ -2457,7 +2458,8 @@
                 return WindowInsets.CONSUMED;
             });
             if (mControlsUiController != null) {
-                mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+                mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+                        true /* startedFromGlobalActions */);
             }
 
             mBackgroundDrawable.setAlpha(0);
@@ -2632,7 +2634,8 @@
             initializeLayout();
             mGlobalActionsLayout.updateList();
             if (mControlsUiController != null) {
-                mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+                mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+                        true /* startedFromGlobalActions */);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
new file mode 100644
index 0000000..3a06f7a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -0,0 +1,155 @@
+/*
+ * 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.systemui.keyguard;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+/**
+ * Data class containing display information (message, icon, styling) for indication to show at
+ * the bottom of the keyguard.
+ *
+ * See {@link com.android.systemui.statusbar.phone.KeyguardBottomAreaView}.
+ */
+public class KeyguardIndication {
+    @NonNull
+    private final CharSequence mMessage;
+    @NonNull
+    private final ColorStateList mTextColor;
+    @Nullable
+    private final Drawable mIcon;
+    @Nullable
+    private final View.OnClickListener mOnClickListener;
+    @Nullable
+    private final Drawable mBackground;
+
+    private KeyguardIndication(
+            CharSequence message,
+            ColorStateList textColor,
+            Drawable icon,
+            View.OnClickListener onClickListener,
+            Drawable background) {
+        mMessage = message;
+        mTextColor = textColor;
+        mIcon = icon;
+        mOnClickListener = onClickListener;
+        mBackground = background;
+    }
+
+    /**
+     * Message to display
+     */
+    public @NonNull CharSequence getMessage() {
+        return mMessage;
+    }
+
+    /**
+     * TextColor to display the message.
+     */
+    public @NonNull ColorStateList getTextColor() {
+        return mTextColor;
+    }
+
+    /**
+     * Icon to display.
+     */
+    public @Nullable Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Click listener for messsage.
+     */
+    public @Nullable View.OnClickListener getClickListener() {
+        return mOnClickListener;
+    }
+
+    /**
+     * Background for textView.
+     */
+    public @Nullable Drawable getBackground() {
+        return mBackground;
+    }
+
+    /**
+     * KeyguardIndication Builder
+     */
+    public static class Builder {
+        private CharSequence mMessage;
+        private Drawable mIcon;
+        private View.OnClickListener mOnClickListener;
+        private ColorStateList mTextColor;
+        private Drawable mBackground;
+
+        public Builder() { }
+
+        /**
+         * Required field. Message to display.
+         */
+        public Builder setMessage(@NonNull CharSequence message) {
+            this.mMessage = message;
+            return this;
+        }
+
+        /**
+         * Required field. Text color to use to display the message.
+         */
+        public Builder setTextColor(@NonNull ColorStateList textColor) {
+            this.mTextColor = textColor;
+            return this;
+        }
+
+        /**
+         * Optional. Icon to show next to the text. Icon location changes based on language
+         * display direction. For LTR, icon shows to the left of the message. For RTL, icon shows
+         * to the right of the message.
+         */
+        public Builder setIcon(Drawable icon) {
+            this.mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Optional. Set a click listener on the message.
+         */
+        public Builder setClickListener(View.OnClickListener onClickListener) {
+            this.mOnClickListener = onClickListener;
+            return this;
+        }
+
+        /**
+         * Optional. Set a custom background on the TextView.
+         */
+        public Builder setBackground(Drawable background) {
+            this.mBackground = background;
+            return this;
+        }
+
+        /**
+         * Build the KeyguardIndication.
+         */
+        public KeyguardIndication build() {
+            if (mMessage == null) throw new IllegalStateException("message must be set");
+            if (mTextColor == null) throw new IllegalStateException("text color must be set");
+            return new KeyguardIndication(
+                    mMessage, mTextColor, mIcon, mOnClickListener, mBackground);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
new file mode 100644
index 0000000..8c04143
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -0,0 +1,327 @@
+/*
+ * 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.systemui.keyguard;
+
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.view.View;
+
+import androidx.annotation.IntDef;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Rotates through messages to show on the keyguard bottom area on the lock screen
+ * NOTE: This controller should not be used on AoD to avoid waking up the AP too often.
+ */
+public class KeyguardIndicationRotateTextViewController extends
+        ViewController<KeyguardIndicationTextView> implements Dumpable {
+    public static String TAG = "KgIndicationRotatingCtrl";
+    private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
+
+    private final StatusBarStateController mStatusBarStateController;
+    private final float mMaxAlpha;
+    private final ColorStateList mInitialTextColorState;
+
+    // Stores @IndicationType => KeyguardIndication messages
+    private final Map<Integer, KeyguardIndication> mIndicationMessages = new HashMap<>();
+
+    // Executor that will show the next message after a delay
+    private final DelayableExecutor mExecutor;
+    @Nullable private ShowNextIndication mShowNextIndicationRunnable;
+
+    // List of indication types to show. The next indication to show is always at index 0
+    private final List<Integer> mIndicationQueue = new LinkedList<>();
+    private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
+
+    private boolean mIsDozing;
+
+    public KeyguardIndicationRotateTextViewController(
+            KeyguardIndicationTextView view,
+            @Main DelayableExecutor executor,
+            StatusBarStateController statusBarStateController,
+            int lockScreenMode
+    ) {
+        super(view);
+        mMaxAlpha = view.getAlpha();
+        mExecutor = executor;
+        mInitialTextColorState = mView != null
+                ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+        mStatusBarStateController = statusBarStateController;
+        mView.setLockScreenMode(lockScreenMode);
+        init();
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mStatusBarStateController.removeCallback(mStatusBarStateListener);
+        cancelScheduledIndication();
+    }
+
+    /**
+     * Update the indication type with the given String.
+     * @param type of indication
+     * @param newIndication message to associate with this indication type
+     * @param showImmediately if true: shows this indication message immediately. Else, the text
+     *                        associated with this type is updated and will show when its turn in
+     *                        the IndicationQueue comes around.
+     */
+    public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
+            boolean showImmediately) {
+        final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
+        final boolean hasNewIndication = newIndication != null
+                && !TextUtils.isEmpty(newIndication.getMessage());
+        if (!hasNewIndication) {
+            mIndicationMessages.remove(type);
+            mIndicationQueue.removeIf(x -> x == type);
+        } else {
+            if (!hasPreviousIndication) {
+                mIndicationQueue.add(type);
+            }
+
+            mIndicationMessages.put(type, newIndication);
+        }
+
+        if (mIsDozing) {
+            return;
+        }
+
+        final boolean showNow = showImmediately
+                || mCurrIndicationType == INDICATION_TYPE_NONE
+                || mCurrIndicationType == type;
+        if (hasNewIndication) {
+            if (showNow) {
+                showIndication(type);
+            } else if (!isNextIndicationScheduled()) {
+                scheduleShowNextIndication();
+            }
+            return;
+        }
+
+        if (mCurrIndicationType == type
+                && !hasNewIndication
+                && showImmediately) {
+            if (mShowNextIndicationRunnable != null) {
+                mShowNextIndicationRunnable.runImmediately();
+            } else {
+                showIndication(INDICATION_TYPE_NONE);
+            }
+        }
+    }
+
+    /**
+     * Stop showing the following indication type.
+     *
+     * If the current indication is of this type, immediately stops showing the message.
+     */
+    public void hideIndication(@IndicationType int type) {
+        updateIndication(type, null, true);
+    }
+
+    /**
+     * Show a transient message.
+     * Transient messages:
+     * - show immediately
+     * - will continue to be in the rotation of messages shown until hideTransient is called.
+     * - can be presented with an "error" color if isError is true
+     */
+    public void showTransient(CharSequence newIndication, boolean isError) {
+        updateIndication(INDICATION_TYPE_TRANSIENT,
+                new KeyguardIndication.Builder()
+                        .setMessage(newIndication)
+                        .setTextColor(isError
+                                ? Utils.getColorError(getContext())
+                                : mInitialTextColorState)
+                        .build(),
+                /* showImmediately */true);
+    }
+
+    /**
+     * Hide a transient message immediately.
+     */
+    public void hideTransient() {
+        hideIndication(INDICATION_TYPE_TRANSIENT);
+    }
+
+    /**
+     * @return true if there are available indications to show
+     */
+    public boolean hasIndications() {
+        return mIndicationMessages.keySet().size() > 0;
+    }
+
+    /**
+     * Immediately show the passed indication type and schedule the next indication to show.
+     * Will re-add this indication to be re-shown after all other indications have been
+     * rotated through.
+     */
+    private void showIndication(@IndicationType int type) {
+        cancelScheduledIndication();
+
+        mCurrIndicationType = type;
+        mIndicationQueue.removeIf(x -> x == type);
+        if (mCurrIndicationType == INDICATION_TYPE_NONE) {
+            mView.setVisibility(View.GONE);
+        } else {
+            mView.setVisibility(View.VISIBLE);
+            mIndicationQueue.add(type); // re-add to show later
+        }
+
+        // pass the style update to be run right before our new indication is shown:
+        mView.switchIndication(mIndicationMessages.get(type));
+
+        // only schedule next indication if there's more than just this indication in the queue
+        if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) {
+            scheduleShowNextIndication();
+        }
+    }
+
+    protected boolean isNextIndicationScheduled() {
+        return mShowNextIndicationRunnable != null;
+    }
+
+    private void scheduleShowNextIndication() {
+        cancelScheduledIndication();
+        mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH);
+    }
+
+    private void cancelScheduledIndication() {
+        if (mShowNextIndicationRunnable != null) {
+            mShowNextIndicationRunnable.cancelDelayedExecution();
+            mShowNextIndicationRunnable = null;
+        }
+    }
+
+    private StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onDozeAmountChanged(float linear, float eased) {
+                    mView.setAlpha((1 - linear) * mMaxAlpha);
+                }
+
+                @Override
+                public void onDozingChanged(boolean isDozing) {
+                    if (isDozing == mIsDozing) return;
+                    mIsDozing = isDozing;
+                    if (mIsDozing) {
+                        showIndication(INDICATION_TYPE_NONE);
+                    } else if (mIndicationQueue.size() > 0) {
+                        showIndication(mIndicationQueue.remove(0));
+                    }
+                }
+            };
+
+    /**
+     * Shows the next indication in the IndicationQueue after an optional delay.
+     * This wrapper has the ability to cancel itself (remove runnable from DelayableExecutor) or
+     * immediately run itself (which also removes itself from the DelayableExecutor).
+     */
+    class ShowNextIndication {
+        private final Runnable mShowIndicationRunnable;
+        private Runnable mCancelDelayedRunnable;
+
+        ShowNextIndication(long delay) {
+            mShowIndicationRunnable = () -> {
+                int type = mIndicationQueue.size() == 0
+                        ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0);
+                showIndication(type);
+            };
+            mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay);
+        }
+
+        public void runImmediately() {
+            cancelDelayedExecution();
+            mShowIndicationRunnable.run();
+        }
+
+        public void cancelDelayedExecution() {
+            if (mCancelDelayedRunnable != null) {
+                mCancelDelayedRunnable.run();
+                mCancelDelayedRunnable = null;
+            }
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardIndicationRotatingTextViewController:");
+        pw.println("    currentMessage=" + mView.getText());
+        pw.println("    dozing:" + mIsDozing);
+        pw.println("    queue:" + mIndicationQueue.toString());
+        pw.println("    showNextIndicationRunnable:" + mShowNextIndicationRunnable);
+
+        if (hasIndications()) {
+            pw.println("    All messages:");
+            for (int type : mIndicationMessages.keySet()) {
+                pw.println("        type=" + type
+                        + " message=" + mIndicationMessages.get(type).getMessage());
+            }
+        }
+    }
+
+    private static final int INDICATION_TYPE_NONE = -1;
+    public static final int INDICATION_TYPE_OWNER_INFO = 0;
+    public static final int INDICATION_TYPE_DISCLOSURE = 1;
+    public static final int INDICATION_TYPE_LOGOUT = 2;
+    public static final int INDICATION_TYPE_BATTERY = 3;
+    public static final int INDICATION_TYPE_ALIGNMENT = 4;
+    public static final int INDICATION_TYPE_TRANSIENT = 5;
+    public static final int INDICATION_TYPE_TRUST = 6;
+    public static final int INDICATION_TYPE_RESTING = 7;
+    public static final int INDICATION_TYPE_USER_LOCKED = 8;
+    public static final int INDICATION_TYPE_NOW_PLAYING = 9;
+    public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
+
+    @IntDef({
+            INDICATION_TYPE_NONE,
+            INDICATION_TYPE_DISCLOSURE,
+            INDICATION_TYPE_OWNER_INFO,
+            INDICATION_TYPE_LOGOUT,
+            INDICATION_TYPE_BATTERY,
+            INDICATION_TYPE_ALIGNMENT,
+            INDICATION_TYPE_TRANSIENT,
+            INDICATION_TYPE_TRUST,
+            INDICATION_TYPE_RESTING,
+            INDICATION_TYPE_USER_LOCKED,
+            INDICATION_TYPE_NOW_PLAYING,
+            INDICATION_TYPE_REVERSE_CHARGING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicationType{}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 
+import android.app.ActivityTaskManager;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
@@ -26,8 +32,17 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
     static final String TAG = "KeyguardService";
     static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     *
+     * Note: Must be consistent with WindowManagerService.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    private static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private final KeyguardViewMediator mKeyguardViewMediator;
     private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
 
@@ -52,6 +82,21 @@
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+        if (sEnableRemoteKeyguardAnimation) {
+            RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+            final RemoteAnimationAdapter exitAnimationAdapter =
+                    new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    exitAnimationAdapter);
+            final RemoteAnimationAdapter occludeAnimationAdapter =
+                    new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+            ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+                    DEFAULT_DISPLAY, definition);
+        }
     }
 
     @Override
@@ -76,6 +121,48 @@
         }
     }
 
+    private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+            checkPermission();
+            mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+                    null /* nonApps */, finishedCallback);
+            Trace.endSection();
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
+    private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                       RemoteAnimationTarget[] apps,
+                       RemoteAnimationTarget[] wallpapers,
+                        RemoteAnimationTarget[] nonApps,
+                        IRemoteAnimationFinishedCallback finishedCallback) {
+            // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+            // run animation.
+            try {
+                finishedCallback.onAnimationFinished();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "RemoteException");
+            }
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
 
         @Override // Binder interface
@@ -225,6 +312,11 @@
             mKeyguardViewMediator.onBootCompleted();
         }
 
+        /**
+         * @deprecated When remote animation is enabled, this won't be called anymore. Use
+         * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+         */
+        @Deprecated
         @Override
         public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
             Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..c55fdf4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
@@ -64,9 +67,15 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -85,6 +94,7 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
 import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -297,6 +307,13 @@
      */
     private final SparseIntArray mLastSimStates = new SparseIntArray();
 
+    /**
+     * Indicates if a SIM card had the SIM PIN enabled during the initialization, before
+     * reaching the SIM_STATE_READY state. The flag is reset to false at SIM_STATE_READY.
+     * Index is the slotId - in case of multiple SIM cards.
+     */
+    private final SparseBooleanArray mSimWasLocked = new SparseBooleanArray();
+
     private boolean mDeviceInteractive;
     private boolean mGoingToSleep;
 
@@ -468,10 +485,10 @@
                 }
             }
 
-            boolean simWasLocked;
+            boolean lastSimStateWasLocked;
             synchronized (KeyguardViewMediator.this) {
                 int lastState = mLastSimStates.get(slotId);
-                simWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
+                lastSimStateWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
                         || lastState == TelephonyManager.SIM_STATE_PUK_REQUIRED);
                 mLastSimStates.append(slotId, simState);
             }
@@ -495,17 +512,19 @@
                         if (simState == TelephonyManager.SIM_STATE_ABSENT) {
                             // MVNO SIMs can become transiently NOT_READY when switching networks,
                             // so we should only lock when they are ABSENT.
-                            if (simWasLocked) {
+                            if (lastSimStateWasLocked) {
                                 if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
                                         + "previous state was locked. Reset the state.");
                                 resetStateLocked();
                             }
+                            mSimWasLocked.append(slotId, false);
                         }
                     }
                     break;
                 case TelephonyManager.SIM_STATE_PIN_REQUIRED:
                 case TelephonyManager.SIM_STATE_PUK_REQUIRED:
                     synchronized (KeyguardViewMediator.this) {
+                        mSimWasLocked.append(slotId, true);
                         if (!mShowing) {
                             if (DEBUG_SIM_STATES) Log.d(TAG,
                                     "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
@@ -532,9 +551,10 @@
                 case TelephonyManager.SIM_STATE_READY:
                     synchronized (KeyguardViewMediator.this) {
                         if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
-                        if (mShowing && simWasLocked) {
+                        if (mShowing && mSimWasLocked.get(slotId, false)) {
                             if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
-                                    + "previous state was locked. Reset the state.");
+                                    + "previously was locked. Reset the state.");
+                            mSimWasLocked.append(slotId, false);
                             resetStateLocked();
                         }
                     }
@@ -1318,6 +1338,7 @@
             if (mHiding && isOccluded) {
                 // We're in the process of going away but WindowManager wants to show a
                 // SHOW_WHEN_LOCKED activity instead.
+                // TODO(bc-unlock): Migrate to remote animation.
                 startKeyguardExitAnimation(0, 0);
             }
 
@@ -1703,7 +1724,9 @@
                     Trace.beginSection(
                             "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
-                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+                            params.mApps, params.mWallpapers, params.mNonApps,
+                            params.mFinishedCallback);
                     mFalsingCollector.onSuccessfulUnlock();
                     Trace.endSection();
                     break;
@@ -1990,15 +2013,19 @@
             if (mShowing && !mOccluded) {
                 mKeyguardGoingAwayRunnable.run();
             } else {
+                // TODO(bc-unlock): Fill parameters
                 handleStartKeyguardExitAnimation(
                         SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
-                        mHideAnimation.getDuration());
+                        mHideAnimation.getDuration(), null /* apps */,  null /* wallpapers */,
+                        null /* nonApps */, null /* finishedCallback */);
             }
         }
         Trace.endSection();
     }
 
-    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
         if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2058,49 @@
             mWakeAndUnlocking = false;
             mDismissCallbackRegistry.notifyDismissSucceeded();
             mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+            // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+            // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+            // supported, so it's always null.
+            mContext.getMainExecutor().execute(() -> {
+                if (finishedCallback == null) {
+                    return;
+                }
+
+                // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+                final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+                        mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+                final RemoteAnimationTarget primary = apps[0];
+                ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+                anim.setDuration(400 /* duration */);
+                anim.setInterpolator(Interpolators.LINEAR);
+                anim.addUpdateListener((ValueAnimator animation) -> {
+                    SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+                            .withAlpha(animation.getAnimatedFraction())
+                            .build();
+                    applier.scheduleApply(params);
+                });
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+                });
+                anim.start();
+            });
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
             adjustStatusBarLocked();
@@ -2223,10 +2293,55 @@
         return mKeyguardViewControllerLazy.get();
     }
 
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+     * animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @deprecated Will be migrate to remote animation soon.
+     */
+    @Deprecated
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+     *
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+            IRemoteAnimationFinishedCallback finishedCallback) {
+        startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and start running keyguard exit animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
         Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
-                new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+                new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+                        wallpapers, nonApps, finishedCallback));
         mHandler.sendMessage(msg);
         Trace.endSection();
     }
@@ -2300,12 +2415,26 @@
 
     private static class StartKeyguardExitAnimParams {
 
+        @WindowManager.TransitionOldType int mTransit;
         long startTime;
         long fadeoutDuration;
+        RemoteAnimationTarget[] mApps;
+        RemoteAnimationTarget[] mWallpapers;
+        RemoteAnimationTarget[] mNonApps;
+        IRemoteAnimationFinishedCallback mFinishedCallback;
 
-        private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+        private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+                long startTime, long fadeoutDuration,
+                RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            this.mTransit = transit;
             this.startTime = startTime;
             this.fadeoutDuration = fadeoutDuration;
+            this.mApps = apps;
+            this.mWallpapers = wallpapers;
+            this.mNonApps = nonApps;
+            this.mFinishedCallback = finishedCallback;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
index 453e85a..517f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
@@ -51,7 +51,7 @@
      * Reload the drawable from resource id, should reapply the previous dark intensity.
      */
     public void updateIcon(int lightIconColor, int darkIconColor) {
-        if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
+        if (mIconResId == 0) {
             return;
         }
         final KeyButtonDrawable currentDrawable = getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..c67aef6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -36,15 +34,15 @@
 import android.util.Log;
 import android.view.ViewGroup;
 
-import androidx.preference.PreferenceManager;
-
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.List;
 
+import javax.inject.Inject;
+
 /**
  * Shows the user their tiles for their priority People (go/live-status).
  */
@@ -59,10 +57,17 @@
     private LauncherApps mLauncherApps;
     private Context mContext;
     private AppWidgetManager mAppWidgetManager;
+    private NotificationEntryManager mNotificationEntryManager;
     private int mAppWidgetId;
     private boolean mShowSingleConversation;
     private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
+    @Inject
+    public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
+        super();
+        mNotificationEntryManager = notificationEntryManager;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -96,8 +101,8 @@
      */
     private void setTileViewsWithPriorityConversations() {
         try {
-            List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(
-                    mContext, mNotificationManager, mPeopleManager, mLauncherApps);
+            List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
+                    mPeopleManager, mLauncherApps, mNotificationEntryManager);
             for (PeopleSpaceTile tile : tiles) {
                 PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
                         tile.getId());
@@ -128,26 +133,17 @@
 
     /** Stores the user selected configuration for {@code mAppWidgetId}. */
     private void storeWidgetConfiguration(PeopleSpaceTile tile) {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
         if (PeopleSpaceUtils.DEBUG) {
             Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
                     + tile.getId() + " for widget ID: "
                     + mAppWidgetId);
         }
-        // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
-        editor.putString(String.valueOf(mAppWidgetId), tile.getId());
-        editor.putInt(tile.getId(), mAppWidgetId);
-        editor.apply();
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-        Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
-        appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
-        int[] widgetIds = appWidgetManager.getAppWidgetIds(
-                new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+        PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+        int[] widgetIds = new int[mAppWidgetId];
         // TODO: Populate new widget with existing conversation notification, if there is any.
         PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
-                mNotificationManager);
+                mPeopleManager);
         finishActivity();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..dd05484 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
 
 import static android.app.Notification.EXTRA_MESSAGES;
 
+import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -59,9 +60,12 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.ArrayUtils;
 import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.people.widget.LaunchConversationActivity;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.text.SimpleDateFormat;
 import java.time.Duration;
@@ -70,11 +74,13 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -89,6 +95,13 @@
     private static final int MIN_HOUR = 1;
     private static final int ONE_DAY = 1;
     public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+    public static final String PACKAGE_NAME = "package_name";
+    public static final String USER_ID = "user_id";
+    public static final String SHORTCUT_ID = "shortcut_id";
+
+    public static final String EMPTY_STRING = "";
+    public static final int INVALID_WIDGET_ID = -1;
+    public static final int INVALID_USER_ID = -1;
 
     private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
     private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -127,7 +140,7 @@
     /** Returns a list of map entries corresponding to user's conversations. */
     public static List<PeopleSpaceTile> getTiles(
             Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
-            LauncherApps launcherApps)
+            LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
             throws Exception {
         boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
@@ -163,6 +176,8 @@
                     getSortedTiles(peopleManager, launcherApps, mergedStream);
             tiles.addAll(recentTiles);
         }
+
+        tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
         return tiles;
     }
 
@@ -171,87 +186,217 @@
      * notification being posted or removed.
      */
     public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
-        IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
-                ServiceManager.getService(Context.PEOPLE_SERVICE));
-        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
-        try {
-            List<PeopleSpaceTile> tiles =
-                    PeopleSpaceUtils.getTiles(context, notificationManager,
-                            peopleManager, launcherApps);
-            for (int appWidgetId : appWidgetIds) {
-                String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
-                if (DEBUG) {
-                    Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
-                }
-
-                Optional<PeopleSpaceTile> entry = tiles.stream().filter(
-                        e -> e.getId().equals(shortcutId)).findFirst();
-
-                if (!entry.isPresent() || shortcutId == null) {
-                    if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
-                    //TODO: Delete app widget id when crash is fixed (b/175486868)
-                    continue;
-                }
-                // Augment current tile based on stored fields.
-                PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
-                        appWidgetId);
-
-                RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
-                // Tell the AppWidgetManager to perform an update on the current app widget.
-                appWidgetManager.updateAppWidget(appWidgetId, views);
-
-                widgetIdToTile.put(appWidgetId, tile);
+        for (int appWidgetId : appWidgetIds) {
+            PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                    appWidgetId);
+            if (tile == null) {
+                if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+                //TODO: Delete app widget id when crash is fixed (b/172932636)
+                continue;
             }
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+            if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+            RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+            // Tell the AppWidgetManager to perform an update on the current app widget.
+            appWidgetManager.updateAppWidget(appWidgetId, views);
+
+            widgetIdToTile.put(appWidgetId, tile);
         }
         getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
     }
 
-    /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
-    @VisibleForTesting
-    static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
-            AppWidgetManager appWidgetManager, int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
-        if (storedTile == null) {
-            return tile;
+    @Nullable
+    private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+            AppWidgetManager appWidgetManager,
+            Context context, int appWidgetId) {
+        try {
+            // Migrate storage for existing users.
+            SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                    Context.MODE_PRIVATE);
+            String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+            int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+            if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+                shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+                if (shortcutId == null) {
+                    Log.e(TAG, "Cannot restore widget");
+                    return null;
+                }
+                migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+                pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            }
+
+            // Check if tile is cached.
+            Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+            PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+            if (tile != null) {
+                return tile;
+            }
+
+            // If tile is null, we need to retrieve from persisted storage.
+            if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+            LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+            ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+            if (channel == null) {
+                Log.d(TAG, "Could not retrieve conversation from storage");
+                return null;
+            }
+            return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+            return null;
         }
-        return tile.toBuilder()
-                .setBirthdayText(storedTile.getBirthdayText())
-                .setNotificationKey(storedTile.getNotificationKey())
-                .setNotificationContent(storedTile.getNotificationContent())
-                .setNotificationDataUri(storedTile.getNotificationDataUri())
-                .build();
     }
 
-    /** If incoming notification changed tile, store the changes in the tile options. */
-    public static void storeNotificationChange(StatusBarNotification sbn,
+    /** Best-effort attempts to migrate existing users to the new storage format. */
+    // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+    private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+            int appWidgetId) {
+        try {
+            List<PeopleSpaceTile> tiles =
+                    PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+                            IPeopleManager.Stub.asInterface(
+                                    ServiceManager.getService(Context.PEOPLE_SERVICE)),
+                            context.getSystemService(LauncherApps.class),
+                            Dependency.get(NotificationEntryManager.class));
+            Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+                    e -> e.getId().equals(shortcutId)).findFirst();
+            if (entry.isPresent()) {
+                if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+                setStorageForTile(context, entry.get(), appWidgetId);
+            } else {
+                Log.e(TAG, "Could not migrate user");
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Could not query conversations");
+        }
+    }
+
+    /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+    public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+        // Write relevant persisted storage.
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+        widgetEditor.apply();
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(appWidgetId), tile.getId());
+        String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+        // Don't overwrite existing widgets with the same key.
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(appWidgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+
+        // Write cached storage.
+        updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+                tile);
+    }
+
+    /** Removes stored data when tile is deleted. */
+    public static void removeStorageForTile(Context context, int widgetId) {
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        String packageName = widgetSp.getString(PACKAGE_NAME, null);
+        String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+        int userId = widgetSp.getInt(USER_ID, -1);
+
+        // Delete widgetId mapping to key.
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.remove(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.remove(String.valueOf(widgetId));
+        editor.apply();
+
+        // Delete all data specifically mapped to widgetId.
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.remove(PACKAGE_NAME);
+        widgetEditor.remove(USER_ID);
+        widgetEditor.remove(SHORTCUT_ID);
+        widgetEditor.apply();
+    }
+
+    /**
+     * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+     * requested update data.
+     */
+    public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+            String packageName, int userId) {
+        SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+        int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+        String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+        return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+                && storedUserId == userId;
+    }
+
+    static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
+            NotificationEntryManager notificationEntryManager) {
+        if (notificationEntryManager == null) {
+            Log.w(TAG, "NotificationEntryManager is null");
+            return tiles;
+        }
+        Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+                .getVisibleNotifications()
+                .stream()
+                .filter(entry -> entry.getRanking() != null
+                        && entry.getRanking().getConversationShortcutInfo() != null)
+                .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+        if (DEBUG) {
+            Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
+        }
+        return tiles
+                .stream()
+                .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+                .collect(Collectors.toList());
+    }
+
+    static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
+            Map<String, NotificationEntry> visibleNotifications) {
+        String shortcutId = tile.getId();
+        String packageName = tile.getPackageName();
+        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        String key = getKey(shortcutId, packageName, userId);
+        if (!visibleNotifications.containsKey(key)) {
+            if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+            return tile;
+        }
+        if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+        return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+    }
+
+    /**
+     * If incoming notification changed tile, store the changes in the tile options.
+     */
+    public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+            Context context,
+            StatusBarNotification sbn,
             NotificationAction notificationAction, AppWidgetManager appWidgetManager,
             int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                appWidgetId);
         if (storedTile == null) {
             if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
             return;
         }
         if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
             if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
-            Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
-            if (message == null) {
-                if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
-                return;
-            }
-            storedTile = storedTile
-                    .toBuilder()
-                    .setNotificationKey(sbn.getKey())
-                    .setNotificationContent(message.getText())
-                    .setNotificationDataUri(message.getDataUri())
-                    .build();
+            storedTile = augmentTileFromNotification(storedTile, sbn);
         } else {
             if (DEBUG) {
                 Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -263,7 +408,22 @@
                     .setNotificationDataUri(null)
                     .build();
         }
-        updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+        updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
+    }
+
+    static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+            StatusBarNotification sbn) {
+        Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
+        if (message == null) {
+            if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+            return tile;
+        }
+        return tile
+                .toBuilder()
+                .setNotificationKey(sbn.getKey())
+                .setNotificationContent(message.getText())
+                .setNotificationDataUri(message.getDataUri())
+                .build();
     }
 
     private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +837,33 @@
         }
         return lookupKeysWithBirthdaysToday;
     }
+
+    static String getKey(NotificationEntry entry) {
+        if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
+                || entry.getSbn() == null || entry.getSbn().getUser() == null) {
+            return null;
+        }
+        return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
+                entry.getSbn().getPackageName(),
+                entry.getSbn().getUser().getIdentifier());
+    }
+
+    /**
+     * Returns the uniquely identifying key for the conversation.
+     *
+     * <p>{@code userId} will always be a number, so we put user ID as the
+     * delimiter between the app-provided strings of shortcut ID and package name.
+     *
+     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+     * a {@code packageName} to always start with a letter. This restriction means we are
+     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+     * case is impossible given the package name restrictions:
+     * <ul>
+     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+     *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+     * </ul>
+     */
+    public static String getKey(String shortcutId, String packageName, int userId) {
+        return shortcutId + "/" + userId + "/" + packageName;
+    }
 }
\ No newline at end of file
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 ad6046b..bee9889 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +29,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -51,7 +53,7 @@
     private final Context mContext;
     private IAppWidgetService mAppWidgetService;
     private AppWidgetManager mAppWidgetManager;
-    private INotificationManager mNotificationManager;
+    private IPeopleManager mPeopleManager;
 
     @Inject
     public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
         mContext = context;
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = AppWidgetManager.getInstance(context);
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mPeopleManager = IPeopleManager.Stub.asInterface(
+                ServiceManager.getService(Context.PEOPLE_SERVICE));
     }
 
-    /** Constructor used for testing. */
+    /**
+     * Constructor used for testing.
+     */
     @VisibleForTesting
     protected PeopleSpaceWidgetManager(Context context) {
         if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
                 ServiceManager.getService(Context.APPWIDGET_SERVICE));
     }
 
-    /** AppWidgetManager setter used for testing. */
+    /**
+     * AppWidgetManager setter used for testing.
+     */
     @VisibleForTesting
     protected void setAppWidgetManager(IAppWidgetService appWidgetService,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = appWidgetManager;
-        mNotificationManager = notificationManager;
+        mPeopleManager = peopleManager;
     }
 
-    /** Updates People Space widgets. */
+    /**
+     * Updates People Space widgets.
+     */
     public void updateWidgets() {
         try {
             if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
 
             if (showSingleConversation) {
                 PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
-                        mAppWidgetManager, mNotificationManager);
+                        mAppWidgetManager, mPeopleManager);
             } else {
                 mAppWidgetService
                         .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,41 @@
      * Check if any existing People tiles match the incoming notification change, and store the
      * change in the tile if so.
      */
-    public void storeNotificationChange(StatusBarNotification sbn,
+    public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
-        if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+        RemoteViews views = new RemoteViews(
+                mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+        if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
         boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (!showSingleConversation) {
             return;
         }
         try {
+            String sbnShortcutId = sbn.getShortcutId();
+            if (sbnShortcutId == null) {
+                if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
+                return;
+            }
             int[] widgetIds = mAppWidgetService.getAppWidgetIds(
                     new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
             );
             if (widgetIds.length == 0) {
+                Log.d(TAG, "No app widget ids returned");
                 return;
             }
-
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-            for (int widgetId : widgetIds) {
-                String shortcutId = sp.getString(String.valueOf(widgetId), null);
-                if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
-                    continue;
-                }
+            int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+            String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+            Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+            if (storedWidgetIds.isEmpty()) {
+                Log.d(TAG, "No stored widget ids");
+                return;
+            }
+            for (String widgetIdString : storedWidgetIds) {
+                int widgetId = Integer.parseInt(widgetIdString);
                 if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
-                PeopleSpaceUtils.storeNotificationChange(
+                PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
                         sbn, notificationAction, mAppWidgetManager, widgetId);
             }
         } catch (Exception e) {
@@ -159,8 +178,7 @@
         public void onNotificationPosted(
                 StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
         }
 
         @Override
@@ -169,8 +187,7 @@
                 NotificationListenerService.RankingMap rankingMap
         ) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -179,8 +196,7 @@
                 NotificationListenerService.RankingMap rankingMap,
                 int reason) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -207,4 +223,4 @@
         }
     };
 
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
@@ -53,8 +53,8 @@
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (showSingleConversation) {
             PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
-                    appWidgetManager, INotificationManager.Stub.asInterface(
-                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+                    appWidgetManager, IPeopleManager.Stub.asInterface(
+                            ServiceManager.getService(Context.PEOPLE_SERVICE)));
             return;
         }
         // Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
         for (int widgetId : appWidgetIds) {
             if (DEBUG) Log.d(TAG, "Widget removed");
             mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+            PeopleSpaceUtils.removeStorageForTile(context, widgetId);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index fb33aff..80794cb 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -28,9 +28,11 @@
 import android.widget.RemoteViews;
 import android.widget.RemoteViewsService;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.people.PeopleSpaceTileView;
 import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,6 +44,7 @@
 
     private IPeopleManager mPeopleManager;
     private INotificationManager mNotificationManager;
+    private NotificationEntryManager mNotificationEntryManager;
     private PackageManager mPackageManager;
     private LauncherApps mLauncherApps;
     private List<PeopleSpaceTile> mTiles = new ArrayList<>();
@@ -56,6 +59,7 @@
         if (DEBUG) Log.d(TAG, "onCreate called");
         mNotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mPackageManager = mContext.getPackageManager();
         mPeopleManager = IPeopleManager.Stub.asInterface(
                 ServiceManager.getService(Context.PEOPLE_SERVICE));
@@ -70,7 +74,7 @@
     private void setTileViewsWithPriorityConversations() {
         try {
             mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
-                    mPeopleManager, mLauncherApps);
+                    mPeopleManager, mLauncherApps, mNotificationEntryManager);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve conversations", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 3f79be8..680a617 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -41,13 +41,12 @@
  * @param context A context to create the dialog
  * @param list list of elements to show in the dialog. The elements will show in the same order they
  *             appear in the list
- * @param activityStarter a callback to start an activity for a given permission group name (as
- *                        given by [PrivacyType.permGroupName])
+ * @param activityStarter a callback to start an activity for a given package name and user id
  */
 class PrivacyDialog(
     context: Context,
     private val list: List<PrivacyElement>,
-    activityStarter: (String) -> Unit
+    activityStarter: (String, Int) -> Unit
 ) : SystemUIDialog(context, R.style.ScreenRecord) {
 
     private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
@@ -129,7 +128,7 @@
         } ?: firstLine
         newView.requireViewById<TextView>(R.id.text).text = finalText
         newView.apply {
-            tag = element.type.permGroupName
+            setTag(element)
             setOnClickListener(clickListener)
         }
         return newView
@@ -152,12 +151,17 @@
     }
 
     private val clickListener = View.OnClickListener { v ->
-        v.tag?.let { activityStarter(it as String) }
+        v.tag?.let {
+            val element = it as PrivacyElement
+            activityStarter(element.packageName, element.userId)
+        }
     }
 
     /** */
     data class PrivacyElement(
         val type: PrivacyType,
+        val packageName: String,
+        val userId: Int,
         val applicationName: CharSequence,
         val attribution: CharSequence?,
         val lastActiveTimestamp: Long,
@@ -169,6 +173,8 @@
 
         init {
             builder.append("type=${type.logName}")
+            builder.append(", packageName=$packageName")
+            builder.append(", userId=$userId")
             builder.append(", appName=$applicationName")
             if (attribution != null) {
                 builder.append(", attribution=$attribution")
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 8b6c04f..03c1843 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -43,7 +43,7 @@
     override fun makeDialog(
         context: Context,
         list: List<PrivacyDialog.PrivacyElement>,
-        starter: (String) -> Unit
+        starter: (String, Int) -> Unit
     ): PrivacyDialog {
         return PrivacyDialog(context, list, starter)
     }
@@ -107,10 +107,11 @@
     }
 
     @MainThread
-    private fun startActivity(permGroupName: String) {
-        val intent = Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
-        intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName)
-        privacyLogger.logStartSettingsActivityFromDialog(permGroupName)
+    private fun startActivity(packageName: String, userId: Int) {
+        val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+        intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+        privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
         if (!keyguardStateController.isUnlocked) {
             // If we are locked, hide the dialog so the user can unlock
             dialog?.hide()
@@ -159,6 +160,8 @@
                         }
                         PrivacyDialog.PrivacyElement(
                                 t,
+                                it.packageName,
+                                UserHandle.getUserId(it.uid),
                                 appName,
                                 it.attribution,
                                 it.lastAccess,
@@ -257,7 +260,7 @@
         fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String) -> Unit
+            starter: (String, Int) -> Unit
         ): PrivacyDialog
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 8059475..ebf6ae9 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -121,11 +121,12 @@
         })
     }
 
-    fun logStartSettingsActivityFromDialog(permGroupName: String) {
+    fun logStartSettingsActivityFromDialog(packageName: String, userId: Int) {
         log(LogLevel.INFO, {
-            str1 = permGroupName
+            str1 = packageName
+            int1 = userId
         }, {
-            "Start settings activity from dialog for perm group: $str1"
+            "Start settings activity from dialog for packageName=$str1, userId=$int1 "
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index d248ab5..c8edaec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,7 +362,7 @@
         if(view == parent || view == null) return;
         // Ignore tile pages as they can have some offset we don't want to take into account in
         // RTL.
-        if (!(view instanceof PagedTileLayout.TilePage)) {
+        if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
             loc1[0] += view.getLeft();
             loc1[1] += view.getTop();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 507048c..21464fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -441,15 +441,17 @@
         return position > mEditIndex;
     }
 
-    private void addFromPosition(int position) {
-        if (!canAddFromPosition(position)) return;
+    private boolean addFromPosition(int position) {
+        if (!canAddFromPosition(position)) return false;
         move(position, mEditIndex);
+        return true;
     }
 
-    private void removeFromPosition(int position) {
-        if (!canRemoveFromPosition(position)) return;
+    private boolean removeFromPosition(int position) {
+        if (!canRemoveFromPosition(position)) return false;
         TileInfo info = mTiles.get(position);
         move(position, info.isSystem ? mEditIndex : mTileDividerIndex);
+        return true;
     }
 
     public SpanSizeLookup getSizeLookup() {
@@ -578,11 +580,17 @@
         }
 
         private void add() {
-            addFromPosition(getLayoutPosition());
+            if (addFromPosition(getLayoutPosition())) {
+                itemView.announceForAccessibility(
+                        itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added));
+            }
         }
 
         private void remove() {
-            removeFromPosition(getLayoutPosition());
+            if (removeFromPosition(getLayoutPosition())) {
+                itemView.announceForAccessibility(
+                        itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed));
+            }
         }
 
         boolean isCurrentTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 9ab2d73..35a8257 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.qs.dagger;
 
+import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
 
@@ -27,6 +30,7 @@
 @Module
 public interface QSFlagsModule {
     String QS_LABELS_FLAG = "qs_labels_flag";
+    String RBC_AVAILABLE = "rbc_available";
 
     @Provides
     @SysUISingleton
@@ -34,4 +38,12 @@
     static boolean provideQSFlag(FeatureFlags featureFlags) {
         return featureFlags.isQSLabelsEnabled();
     }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(RBC_AVAILABLE)
+    static boolean isReduceBrightColorsAvailable(Context context) {
+        return ColorDisplayManager.isReduceBrightColorsAvailable(context);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 6e28cd8..56d06eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -315,7 +315,8 @@
         }
         try {
             if (DEBUG) Log.d(TAG, "Adding token");
-            mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY);
+            mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY,
+                    null /* options */);
             mIsTokenGranted = true;
         } catch (RemoteException e) {
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 84c7611..f94cabc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -16,12 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.text.TextUtils;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -39,6 +40,7 @@
 import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /** Quick settings tile: Reduce Bright Colors **/
 public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@@ -46,9 +48,11 @@
     //TODO(b/170973645): get icon drawable
     private final Icon mIcon = null;
     private final SecureSetting mActivatedSetting;
+    private final boolean mIsAvailable;
 
     @Inject
     public ReduceBrightColorsTile(
+            @Named(RBC_AVAILABLE) boolean isAvailable,
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
@@ -69,11 +73,12 @@
                 refreshState();
             }
         };
+        mIsAvailable = isAvailable;
+
     }
     @Override
     public boolean isAvailable() {
-        // TODO(b/170970675): Call into ColorDisplayService to get availability/config status
-        return true;
+        return mIsAvailable;
     }
 
     @Override
@@ -121,15 +126,6 @@
         state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label);
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.contentDescription = state.label;
-
-        final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mActivatedSetting.getCurrentUser());
-        state.secondaryLabel = state.value ? mContext.getString(
-                R.string.quick_settings_reduce_bright_colors_secondary_label, intensity) : "";
-
-        state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
-                ? state.label
-                : TextUtils.concat(state.label, ", ", state.secondaryLabel);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a9f76f6..5b2a7e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,7 +101,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -149,7 +149,7 @@
     private final ScreenshotHelper mScreenshotHelper;
     private final Optional<OneHanded> mOneHandedOptional;
     private final CommandQueue mCommandQueue;
-    private final Transitions mShellTransitions;
+    private final RemoteTransitions mShellTransitions;
 
     private Region mActiveNavBarRegion;
 
@@ -620,6 +620,19 @@
         }
 
         @Override
+        public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+            if (!verifyCaller("exitSplitScreenOnHide")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void startTask(int taskId, int stage, int position, Bundle options) {
             if (!verifyCaller("startTask")) {
                 return;
@@ -799,7 +812,7 @@
             Optional<Lazy<StatusBar>> statusBarOptionalLazy,
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
-            Transitions shellTransitions) {
+            RemoteTransitions shellTransitions) {
         super(broadcastDispatcher);
         mContext = context;
         mPipOptional = pipOptional;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 9037192..5438743 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -187,7 +187,7 @@
      * @param refreshRate Desired refresh rate
      * @return array with supported width, height, and refresh rate
      */
-    private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) {
+    private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
         double maxScale = 0;
 
         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
@@ -207,25 +207,33 @@
                     int width = vc.getSupportedWidths().getUpper();
                     int height = vc.getSupportedHeights().getUpper();
 
-                    if (width >= screenWidth && height >= screenHeight
-                            && vc.isSizeSupported(screenWidth, screenHeight)) {
+                    int screenWidthAligned = screenWidth;
+                    if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+                        screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+                    }
+                    int screenHeightAligned = screenHeight;
+                    if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+                        screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+                    }
 
+                    if (width >= screenWidthAligned && height >= screenHeightAligned
+                            && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
                         // Desired size is supported, now get the rate
-                        int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight)
-                                .getUpper().intValue();
+                        int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+                                screenHeightAligned).getUpper().intValue();
 
                         if (maxRate < refreshRate) {
                             refreshRate = maxRate;
                         }
                         Log.d(TAG, "Screen size supported at rate " + refreshRate);
-                        return new int[]{screenWidth, screenHeight, refreshRate};
+                        return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
                     }
 
                     // Otherwise, continue searching
                     double scale = Math.min(((double) width / screenWidth),
                             ((double) height / screenHeight));
                     if (scale > maxScale) {
-                        maxScale = scale;
+                        maxScale = Math.min(1, scale);
                         maxInfo = vc;
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 8e182b4..c8afd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -35,7 +35,7 @@
  * cropped out.
  */
 public class CropView extends View {
-    private enum CropBoundary {
+    public enum CropBoundary {
         NONE, TOP, BOTTOM
     }
 
@@ -48,8 +48,14 @@
     private float mTopCrop = 0f;
     private float mBottomCrop = 1f;
 
+    // When the user is dragging a handle, these variables store the distance between the top/bottom
+    // crop values and
+    private float mTopDelta = 0f;
+    private float mBottomDelta = 0f;
+
     private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
-    private float mLastY;
+    private float mStartingY;  // y coordinate of ACTION_DOWN
+    private CropInteractionListener mCropInteractionListener;
 
     public CropView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
@@ -73,54 +79,84 @@
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        drawShade(canvas, 0, mTopCrop);
-        drawShade(canvas, mBottomCrop, 1f);
-        drawHandle(canvas, mTopCrop);
-        drawHandle(canvas, mBottomCrop);
+        float top = mTopCrop + mTopDelta;
+        float bottom = mBottomCrop + mBottomDelta;
+        drawShade(canvas, 0, top);
+        drawShade(canvas, bottom, 1f);
+        drawHandle(canvas, top);
+        drawHandle(canvas, bottom);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         int topPx = fractionToPixels(mTopCrop);
         int bottomPx = fractionToPixels(mBottomCrop);
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
-            if (mCurrentDraggingBoundary != CropBoundary.NONE) {
-                mLastY = event.getY();
-            }
-            return true;
-        }
-        if (event.getAction() == MotionEvent.ACTION_MOVE
-                && mCurrentDraggingBoundary != CropBoundary.NONE) {
-            float delta = event.getY() - mLastY;
-            if (mCurrentDraggingBoundary == CropBoundary.TOP) {
-                mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
-                        bottomPx - 2 * mCropTouchMargin));
-            } else {  // Bottom
-                mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
-                        topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
-            }
-            mLastY = event.getY();
-            invalidate();
-            return true;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+                if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+                    mStartingY = event.getY();
+                    updateListener(event);
+                }
+                return true;
+            case MotionEvent.ACTION_MOVE:
+                if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+                    float delta = event.getY() - mStartingY;
+                    if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+                        mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx,
+                                bottomPx - 2 * mCropTouchMargin - topPx));
+                    } else {  // Bottom
+                        mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
+                                topPx + 2 * mCropTouchMargin - bottomPx,
+                                getMeasuredHeight() - bottomPx));
+                    }
+                    updateListener(event);
+                    invalidate();
+                    return true;
+                }
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+                    // Commit the delta to the stored crop values.
+                    mTopCrop += mTopDelta;
+                    mBottomCrop += mBottomDelta;
+                    mTopDelta = 0;
+                    mBottomDelta = 0;
+                    updateListener(event);
+                }
         }
         return super.onTouchEvent(event);
     }
 
     /**
-     * @return value [0,1] representing the position of the top crop boundary.
+     * @return value [0,1] representing the position of the top crop boundary. Does not reflect
+     * changes from any in-progress touch input.
      */
     public float getTopBoundary() {
         return mTopCrop;
     }
 
     /**
-     * @return value [0,1] representing the position of the bottom crop boundary.
+     * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect
+     * changes from any in-progress touch input.
      */
     public float getBottomBoundary() {
         return mBottomCrop;
     }
 
+    public void setCropInteractionListener(CropInteractionListener listener) {
+        mCropInteractionListener = listener;
+    }
+
+    private void updateListener(MotionEvent event) {
+        if (mCropInteractionListener != null) {
+            float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
+                    ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta;
+            mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary,
+                    boundaryPosition, fractionToPixels(boundaryPosition));
+        }
+    }
+
     private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
         canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
                 fractionToPixels(fracEnd), mShadePaint);
@@ -148,4 +184,17 @@
         }
         return CropBoundary.NONE;
     }
+
+    /**
+     * Listen for crop motion events and state.
+     */
+    public interface CropInteractionListener {
+        /**
+         * Called whenever CropView has a MotionEvent that can impact the position of the crop
+         * boundaries.
+         */
+        void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
+                int boundaryPositionPx);
+
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
new file mode 100644
index 0000000..f887151
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -0,0 +1,184 @@
+/*
+ * 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.systemui.screenshot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and
+ * positioning dereived from events from a CropView to which it listens.
+ *
+ * Not meant to be a general-purpose magnifier!
+ */
+public class MagnifierView extends View implements CropView.CropInteractionListener {
+    private Drawable mDrawable;
+
+    private final Paint mShadePaint;
+    private final Paint mHandlePaint;
+
+    private Path mOuterCircle;
+    private Path mInnerCircle;
+
+    private Path mCheckerboard;
+    private Paint mCheckerboardPaint;
+    private final float mBorderPx;
+    private final int mBorderColor;
+    private float mCheckerboardBoxSize = 40;
+
+    private float mLastCropPosition;
+    private CropView.CropBoundary mCropBoundary;
+
+    public MagnifierView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        TypedArray t = context.getTheme().obtainStyledAttributes(
+                attrs, R.styleable.MagnifierView, 0, 0);
+        mShadePaint = new Paint();
+        mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT));
+        mHandlePaint = new Paint();
+        mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK));
+        mHandlePaint.setStrokeWidth(
+                t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20));
+        mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0);
+        mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE);
+        t.recycle();
+        mCheckerboardPaint = new Paint();
+        mCheckerboardPaint.setColor(Color.GRAY);
+    }
+
+    public void setImageTileset(ImageTileSet tiles) {
+        if (tiles != null) {
+            mDrawable = tiles.getDrawable();
+            mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight());
+        } else {
+            mDrawable = null;
+        }
+        invalidate();
+    }
+
+    @Override
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        int radius = getWidth() / 2;
+        mOuterCircle = new Path();
+        mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW);
+        mInnerCircle = new Path();
+        mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW);
+        mCheckerboard = generateCheckerboard();
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        // TODO: just draw a circle at the end instead of clipping like this?
+        canvas.clipPath(mOuterCircle);
+        canvas.drawColor(mBorderColor);
+        canvas.clipPath(mInnerCircle);
+
+        // Draw a checkerboard pattern for out of bounds.
+        canvas.drawPath(mCheckerboard, mCheckerboardPaint);
+
+        if (mDrawable != null) {
+            canvas.save();
+            // Translate such that the center of this view represents the center of the crop
+            // boundary.
+            canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2,
+                    -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2);
+            mDrawable.draw(canvas);
+            canvas.restore();
+        }
+
+        Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2);
+        if (mCropBoundary == CropView.CropBoundary.BOTTOM) {
+            scrimRect.offset(0, getHeight() / 2);
+        }
+        canvas.drawRect(scrimRect, mShadePaint);
+
+        canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint);
+    }
+
+    @Override
+    public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
+            float cropPosition, int cropPositionPx) {
+        mCropBoundary = boundary;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mLastCropPosition = cropPosition;
+                setTranslationY(cropPositionPx - getHeight() / 2);
+                setPivotX(getWidth() / 2);
+                setPivotY(getHeight() / 2);
+                setScaleX(0.2f);
+                setScaleY(0.2f);
+                setAlpha(0f);
+                setTranslationX((getParentWidth() - getWidth()) / 2);
+                setVisibility(View.VISIBLE);
+                animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mLastCropPosition = cropPosition;
+                setTranslationY(cropPositionPx - getHeight() / 2);
+                invalidate();
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f)
+                        .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start();
+                break;
+        }
+    }
+
+    private Path generateCheckerboard() {
+        Path path = new Path();
+        int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize);
+        int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize);
+
+        for (int row = 0; row < checkerHeight; row++) {
+            // Alternate starting on the first and second column;
+            int colStart = (row % 2 == 0) ? 0 : 1;
+            for (int col = colStart; col < checkerWidth; col += 2) {
+                path.addRect(col * mCheckerboardBoxSize,
+                        row * mCheckerboardBoxSize,
+                        (col + 1) * mCheckerboardBoxSize,
+                        (row + 1) * mCheckerboardBoxSize,
+                        Path.Direction.CW);
+            }
+        }
+        return path;
+    }
+
+    private int getParentWidth() {
+        return ((View) getParent()).getWidth();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 18c379a..25438a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -81,6 +81,7 @@
     private View mEdit;
     private View mShare;
     private CropView mCropView;
+    private MagnifierView mMagnifierView;
 
     public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
             Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -120,13 +121,14 @@
         mEdit = findViewById(R.id.edit);
         mShare = findViewById(R.id.share);
         mCropView = findViewById(R.id.crop_view);
+        mMagnifierView = findViewById(R.id.magnifier);
+        mCropView.setCropInteractionListener(mMagnifierView);
 
         mSave.setOnClickListener(this::onClicked);
         mCancel.setOnClickListener(this::onClicked);
         mEdit.setOnClickListener(this::onClicked);
         mShare.setOnClickListener(this::onClicked);
 
-        //mPreview.setImageDrawable(mImageTileSet.getDrawable());
         mConnection.start(this::startCapture);
     }
 
@@ -164,6 +166,7 @@
 
     private void doFinish() {
         mPreview.setImageDrawable(null);
+        mMagnifierView.setImageTileset(null);
         mImageTileSet.clear();
         mCallback.onFinish();
         mWindow.getDecorView().getViewTreeObserver()
@@ -273,6 +276,7 @@
             session.end(mCallback::onFinish);
         } else {
             mPreview.setImageDrawable(mImageTileSet.getDrawable());
+            mMagnifierView.setImageTileset(mImageTileSet);
         }
     }
 }
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 a3b5f27..43bb343 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -79,6 +79,7 @@
     private final float mMaximumBacklightForVr;
     private final float mDefaultBacklightForVr;
 
+    private final int mDisplayId;
     private final Context mContext;
     private final ToggleSlider mControl;
     private final boolean mAutomaticAvailable;
@@ -311,6 +312,7 @@
         };
         mBrightnessObserver = new BrightnessObserver(mHandler);
 
+        mDisplayId = mContext.getDisplayId();
         PowerManager pm = context.getSystemService(PowerManager.class);
         mMinimumBacklight = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -420,7 +422,7 @@
     }
 
     private void setBrightness(float brightness) {
-        mDisplayManager.setTemporaryBrightness(brightness);
+        mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
     }
 
     private void updateVrMode(boolean isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index e7b60c3..2dd85e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -67,6 +67,10 @@
         return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
     }
 
+    public boolean useNewLockscreenAnimations() {
+        return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
+    }
+
     public boolean isPeopleTileEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_conversations);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c816784..c70a93b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,10 +16,24 @@
 
 package com.android.systemui.statusbar;
 
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -47,6 +61,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -56,13 +71,16 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.KeyguardIndication;
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
 
@@ -76,8 +94,7 @@
  * Controls the indications and error messages shown on the Keyguard
  */
 @SysUISingleton
-public class KeyguardIndicationController implements StateListener,
-        KeyguardStateController.Callback {
+public class KeyguardIndicationController implements KeyguardStateController.Callback {
 
     private static final String TAG = "KeyguardIndication";
     private static final boolean DEBUG_CHARGING_SPEED = false;
@@ -94,14 +111,17 @@
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private ViewGroup mIndicationArea;
-    private KeyguardIndicationTextView mTextView;
-    private KeyguardIndicationTextView mDisclosure;
+    private KeyguardIndicationTextView mTopIndicationView;
     private final IBatteryStats mBatteryInfo;
     private final SettableWakeLock mWakeLock;
     private final DockManager mDockManager;
     private final DevicePolicyManager mDevicePolicyManager;
     private final UserManager mUserManager;
+    private final @Main DelayableExecutor mExecutor;
+    private final LockPatternUtils mLockPatternUtils;
+    private final IActivityManager mIActivityManager;
 
+    protected KeyguardIndicationRotateTextViewController mRotateTextViewController;
     private BroadcastReceiver mBroadcastReceiver;
     private LockscreenLockIconController mLockIconController;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -110,7 +130,7 @@
     private String mAlignmentIndication;
     private CharSequence mTransientIndication;
     private boolean mTransientTextIsError;
-    private ColorStateList mInitialTextColorState;
+    protected ColorStateList mInitialTextColorState;
     private boolean mVisible;
     private boolean mHideTransientMessageOnScreenOff;
 
@@ -124,8 +144,8 @@
     private int mBatteryLevel;
     private boolean mBatteryPresent = true;
     private long mChargingTimeRemaining;
-    private float mDisclosureMaxAlpha;
     private String mMessageToShowOnScreenOn;
+    protected int mLockScreenMode;
 
     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
 
@@ -151,7 +171,8 @@
             BroadcastDispatcher broadcastDispatcher,
             DevicePolicyManager devicePolicyManager,
             IBatteryStats iBatteryStats,
-            UserManager userManager) {
+            UserManager userManager,
+            @Main DelayableExecutor executor) {
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mDevicePolicyManager = devicePolicyManager;
@@ -165,23 +186,29 @@
                 wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG);
         mBatteryInfo = iBatteryStats;
         mUserManager = userManager;
+        mExecutor = executor;
+        mLockPatternUtils = new LockPatternUtils(context);
+        mIActivityManager = ActivityManager.getService();
 
         mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
         mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
-        mStatusBarStateController.addCallback(this);
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
         mKeyguardStateController.addCallback(this);
     }
 
     public void setIndicationArea(ViewGroup indicationArea) {
         mIndicationArea = indicationArea;
-        mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
-        mInitialTextColorState = mTextView != null ?
-                mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
-        mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
-        mDisclosureMaxAlpha = mDisclosure.getAlpha();
+        mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
+        mInitialTextColorState = mTopIndicationView != null
+                ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+        mRotateTextViewController = new KeyguardIndicationRotateTextViewController(
+                indicationArea.findViewById(R.id.keyguard_indication_text_bottom),
+                mExecutor,
+                mStatusBarStateController,
+                mLockScreenMode);
         updateIndication(false /* animate */);
         updateDisclosure();
-
+        updateOwnerInfo();
         if (mBroadcastReceiver == null) {
             // Update the disclosure proactively to avoid IPC on the critical path.
             mBroadcastReceiver = new BroadcastReceiver() {
@@ -233,19 +260,196 @@
         return mUpdateMonitorCallback;
     }
 
+    /**
+     * Doesn't include owner information or disclosure which get triggered separately.
+     */
+    private void updateIndications(boolean animate, int userId) {
+        updateBattery(animate);
+        updateUserLocked(userId);
+        updateTransient();
+        updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication());
+        updateAlignment();
+        updateResting();
+    }
+
     private void updateDisclosure() {
-        // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path.
         if (whitelistIpcs(this::isOrganizationOwnedDevice)) {
-            CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
-            if (organizationName != null) {
-                mDisclosure.switchIndication(mContext.getResources().getString(
-                        R.string.do_disclosure_with_name, organizationName));
-            } else {
-                mDisclosure.switchIndication(R.string.do_disclosure_generic);
-            }
-            mDisclosure.setVisibility(View.VISIBLE);
+            final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
+            final CharSequence disclosure =  organizationName != null
+                    ? mContext.getResources().getString(R.string.do_disclosure_with_name,
+                    organizationName)
+                    : mContext.getResources().getText(R.string.do_disclosure_generic);
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_DISCLOSURE,
+                    new KeyguardIndication.Builder()
+                            .setMessage(disclosure)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    /* updateImmediately */ false);
         } else {
-            mDisclosure.setVisibility(View.GONE);
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE);
+        }
+
+        if (isKeyguardLayoutEnabled()) {
+            updateIndication(false); // resting indication may need to update
+        }
+    }
+
+    private void updateBattery(boolean animate) {
+        if (mPowerPluggedIn || mEnableBatteryDefender) {
+            String powerIndication = computePowerIndication();
+            if (DEBUG_CHARGING_SPEED) {
+                powerIndication += ",  " + (mChargingWattage / 1000) + " mW";
+            }
+
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_BATTERY,
+                    new KeyguardIndication.Builder()
+                            .setMessage(powerIndication)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    animate);
+        } else {
+            // don't show the charging information if device isn't plugged in
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY);
+        }
+    }
+
+    private void updateUserLocked(int userId) {
+        if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_USER_LOCKED,
+                    new KeyguardIndication.Builder()
+                            .setMessage(mContext.getResources().getText(
+                                    com.android.internal.R.string.lockscreen_storage_locked))
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    false);
+        } else {
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED);
+        }
+    }
+
+    private void updateTransient() {
+        if (!TextUtils.isEmpty(mTransientIndication)) {
+            mRotateTextViewController.showTransient(mTransientIndication,
+                    mTransientTextIsError);
+        } else {
+            mRotateTextViewController.hideTransient();
+        }
+    }
+
+    private void updateTrust(int userId, CharSequence trustGrantedIndication,
+            CharSequence trustManagedIndication) {
+        if (!TextUtils.isEmpty(trustGrantedIndication)
+                && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_TRUST,
+                    new KeyguardIndication.Builder()
+                            .setMessage(trustGrantedIndication)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    false);
+        } else if (!TextUtils.isEmpty(trustManagedIndication)
+                && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
+                && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_TRUST,
+                    new KeyguardIndication.Builder()
+                            .setMessage(trustManagedIndication)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    false);
+        } else {
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST);
+        }
+    }
+
+    private void updateAlignment() {
+        if (!TextUtils.isEmpty(mAlignmentIndication)) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_ALIGNMENT,
+                    new KeyguardIndication.Builder()
+                            .setMessage(mAlignmentIndication)
+                            .setTextColor(Utils.getColorError(mContext))
+                            .build(),
+                    true);
+        } else {
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT);
+        }
+    }
+
+    private void updateResting() {
+        if (mRestingIndication != null
+                && !mRotateTextViewController.hasIndications()) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_RESTING,
+                    new KeyguardIndication.Builder()
+                            .setMessage(mRestingIndication)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    false);
+        } else {
+            mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING);
+        }
+    }
+
+    protected boolean isKeyguardLayoutEnabled() {
+        return mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1;
+    }
+
+    private void updateLogoutView() {
+        if (!isKeyguardLayoutEnabled()) {
+            return;
+        }
+        final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled()
+                && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM;
+        String logoutString = shouldShowLogout ? mContext.getResources().getString(
+                    com.android.internal.R.string.global_action_logout) : null;
+        mRotateTextViewController.updateIndication(
+                INDICATION_TYPE_LOGOUT,
+                new KeyguardIndication.Builder()
+                        .setMessage(logoutString)
+                        .setTextColor(mInitialTextColorState)
+                        .setBackground(mContext.getDrawable(
+                                com.android.systemui.R.drawable.logout_button_background))
+                        .setClickListener((view) -> {
+                            int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+                            try {
+                                mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
+                                mIActivityManager.stopUser(currentUserId, true /* force */, null);
+                            } catch (RemoteException re) {
+                                Log.e(TAG, "Failed to logout user", re);
+                            }
+                        })
+                .build(),
+                false);
+        updateIndication(false); // resting indication may need to update
+    }
+
+    private void updateOwnerInfo() {
+        if (!isKeyguardLayoutEnabled()) {
+            return;
+        }
+        String info = mLockPatternUtils.getDeviceOwnerInfo();
+        if (info == null) {
+            // Use the current user owner information if enabled.
+            final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
+                    KeyguardUpdateMonitor.getCurrentUser());
+            if (ownerInfoEnabled) {
+                info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+            }
+        }
+        if (info != null) {
+            mRotateTextViewController.updateIndication(
+                    INDICATION_TYPE_OWNER_INFO,
+                    new KeyguardIndication.Builder()
+                            .setMessage(info)
+                            .setTextColor(mInitialTextColorState)
+                            .build(),
+                    false);
+        } else {
+            updateIndication(false); // resting indication may need to update
         }
     }
 
@@ -281,9 +485,10 @@
         return UserHandle.USER_NULL;
     }
 
-    public void setVisible(boolean visible) {
+    @VisibleForTesting
+    protected void setVisible(boolean visible) {
         mVisible = visible;
-        mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mIndicationArea.setVisibility(visible ? VISIBLE : GONE);
         if (visible) {
             // If this is called after an error message was already shown, we should not clear it.
             // Otherwise the error message won't be shown
@@ -382,6 +587,7 @@
             mTransientIndication = null;
             mHideTransientMessageOnScreenOff = false;
             mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+            mRotateTextViewController.hideTransient();
             updateIndication(false);
         }
     }
@@ -396,98 +602,109 @@
         }
 
         // A few places might need to hide the indication, so always start by making it visible
-        mIndicationArea.setVisibility(View.VISIBLE);
+        mIndicationArea.setVisibility(VISIBLE);
 
         // Walk down a precedence-ordered list of what indication
         // should be shown based on user or device state
+        // AoD
         if (mDozing) {
+            mTopIndicationView.setVisibility(VISIBLE);
             // When dozing we ignore any text color and use white instead, because
             // colors can be hard to read in low brightness.
-            mTextView.setTextColor(Color.WHITE);
+            mTopIndicationView.setTextColor(Color.WHITE);
             if (!TextUtils.isEmpty(mTransientIndication)) {
-                mTextView.switchIndication(mTransientIndication);
+                mTopIndicationView.switchIndication(mTransientIndication, null);
             } else if (!mBatteryPresent) {
                 // If there is no battery detected, hide the indication and bail
-                mIndicationArea.setVisibility(View.GONE);
+                mIndicationArea.setVisibility(GONE);
             } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
-                mTextView.switchIndication(mAlignmentIndication);
-                mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
+                mTopIndicationView.switchIndication(mAlignmentIndication, null);
+                mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
             } else if (mPowerPluggedIn || mEnableBatteryDefender) {
                 String indication = computePowerIndication();
                 if (animate) {
-                    animateText(mTextView, indication);
+                    animateText(mTopIndicationView, indication);
                 } else {
-                    mTextView.switchIndication(indication);
+                    mTopIndicationView.switchIndication(indication, null);
                 }
             } else {
                 String percentage = NumberFormat.getPercentInstance()
                         .format(mBatteryLevel / 100f);
-                mTextView.switchIndication(percentage);
+                mTopIndicationView.switchIndication(percentage, null);
             }
             return;
         }
 
-        int userId = KeyguardUpdateMonitor.getCurrentUser();
-        String trustGrantedIndication = getTrustGrantedIndication();
-        String trustManagedIndication = getTrustManagedIndication();
-
-        String powerIndication = null;
-        if (mPowerPluggedIn || mEnableBatteryDefender) {
-            powerIndication = computePowerIndication();
-        }
-
+        // LOCK SCREEN
         // Some cases here might need to hide the indication (if the battery is not present)
-        boolean hideIndication = false;
-        boolean isError = false;
-        if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
-            mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
-        } else if (!TextUtils.isEmpty(mTransientIndication)) {
-            if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
-                String indication = mContext.getResources().getString(
-                                R.string.keyguard_indication_trust_unlocked_plugged_in,
-                                mTransientIndication, powerIndication);
-                mTextView.switchIndication(indication);
-                hideIndication = !mBatteryPresent;
-            } else {
-                mTextView.switchIndication(mTransientIndication);
-            }
-            isError = mTransientTextIsError;
-        } else if (!TextUtils.isEmpty(trustGrantedIndication)
-                && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
-            if (powerIndication != null) {
-                String indication = mContext.getResources().getString(
-                                R.string.keyguard_indication_trust_unlocked_plugged_in,
-                                trustGrantedIndication, powerIndication);
-                mTextView.switchIndication(indication);
-                hideIndication = !mBatteryPresent;
-            } else {
-                mTextView.switchIndication(trustGrantedIndication);
-            }
-        } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
-            mTextView.switchIndication(mAlignmentIndication);
-            isError = true;
-            hideIndication = !mBatteryPresent;
-        } else if (mPowerPluggedIn || mEnableBatteryDefender) {
-            if (DEBUG_CHARGING_SPEED) {
-                powerIndication += ",  " + (mChargingWattage / 1000) + " mW";
-            }
-            if (animate) {
-                animateText(mTextView, powerIndication);
-            } else {
-                mTextView.switchIndication(powerIndication);
-            }
-            hideIndication = !mBatteryPresent;
-        } else if (!TextUtils.isEmpty(trustManagedIndication)
-                && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
-                && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
-            mTextView.switchIndication(trustManagedIndication);
+        int userId = KeyguardUpdateMonitor.getCurrentUser();
+
+        if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) {
+            mTopIndicationView.setVisibility(GONE);
+            updateIndications(animate, userId);
         } else {
-            mTextView.switchIndication(mRestingIndication);
-        }
-        mTextView.setTextColor(isError ? Utils.getColorError(mContext)
-                : mInitialTextColorState);
-        if (hideIndication) {
-            mIndicationArea.setVisibility(View.GONE);
+            boolean hideIndication = false;
+            boolean isError = false;
+            String trustGrantedIndication = getTrustGrantedIndication();
+            String trustManagedIndication = getTrustManagedIndication();
+            String powerIndication = null;
+
+            if (mPowerPluggedIn || mEnableBatteryDefender) {
+                powerIndication = computePowerIndication();
+            }
+            if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
+                mTopIndicationView.switchIndication(
+                        com.android.internal.R.string.lockscreen_storage_locked);
+            } else if (!TextUtils.isEmpty(mTransientIndication)) {
+                if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
+                    String indication = mContext.getResources().getString(
+                            R.string.keyguard_indication_trust_unlocked_plugged_in,
+                            mTransientIndication, powerIndication);
+                    mTopIndicationView.switchIndication(indication, null);
+                    hideIndication = !mBatteryPresent;
+                } else {
+                    mTopIndicationView.switchIndication(mTransientIndication, null);
+                }
+                isError = mTransientTextIsError;
+            } else if (!TextUtils.isEmpty(trustGrantedIndication)
+                    && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+                if (powerIndication != null) {
+                    String indication = mContext.getResources().getString(
+                            R.string.keyguard_indication_trust_unlocked_plugged_in,
+                            trustGrantedIndication, powerIndication);
+                    mTopIndicationView.switchIndication(indication, null);
+                    hideIndication = !mBatteryPresent;
+                } else {
+                    mTopIndicationView.switchIndication(trustGrantedIndication, null);
+                }
+            } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+                mTopIndicationView.switchIndication(mAlignmentIndication, null);
+                isError = true;
+                hideIndication = !mBatteryPresent;
+            } else if (mPowerPluggedIn || mEnableBatteryDefender) {
+                if (DEBUG_CHARGING_SPEED) {
+                    powerIndication += ",  " + (mChargingWattage / 1000) + " mW";
+                }
+                if (animate) {
+                    animateText(mTopIndicationView, powerIndication);
+                } else {
+                    mTopIndicationView.switchIndication(powerIndication, null);
+                }
+                hideIndication = !mBatteryPresent;
+            } else if (!TextUtils.isEmpty(trustManagedIndication)
+                    && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
+                    && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+                mTopIndicationView.switchIndication(trustManagedIndication, null);
+            } else {
+                mTopIndicationView.switchIndication(mRestingIndication, null);
+            }
+
+            mTopIndicationView.setTextColor(
+                    isError ? Utils.getColorError(mContext) : mInitialTextColorState);
+
+            if (hideIndication) {
+                mIndicationArea.setVisibility(GONE);
+            }
         }
     }
 
@@ -510,7 +727,7 @@
 
                     @Override
                     public void onAnimationStart(Animator animation) {
-                        textView.switchIndication(indication);
+                        textView.switchIndication(indication, null);
                     }
 
                     @Override
@@ -634,18 +851,6 @@
         }
     }
 
-    public void setDozing(boolean dozing) {
-        if (mDozing == dozing) {
-            return;
-        }
-        mDozing = dozing;
-        if (mHideTransientMessageOnScreenOff && mDozing) {
-            hideTransientIndication();
-        } else {
-            updateIndication(false);
-        }
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardIndicationController:");
         pw.println("  mTransientTextIsError: " + mTransientTextIsError);
@@ -659,23 +864,10 @@
         pw.println("  mDozing: " + mDozing);
         pw.println("  mBatteryLevel: " + mBatteryLevel);
         pw.println("  mBatteryPresent: " + mBatteryPresent);
-        pw.println("  mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
+        pw.println("  mTextView.getText(): " + (
+                mTopIndicationView == null ? null : mTopIndicationView.getText()));
         pw.println("  computePowerIndication(): " + computePowerIndication());
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        // don't care
-    }
-
-    @Override
-    public void onDozingChanged(boolean isDozing) {
-        setDozing(isDozing);
-    }
-
-    @Override
-    public void onDozeAmountChanged(float linear, float eased) {
-        mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha);
+        mRotateTextViewController.dump(fd, pw, args);
     }
 
     @Override
@@ -687,6 +879,11 @@
         public static final int HIDE_DELAY_MS = 5000;
 
         @Override
+        public void onLockScreenModeChanged(int mode) {
+            mLockScreenMode = mode;
+        }
+
+        @Override
         public void onRefreshBatteryInfo(BatteryStatus status) {
             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
@@ -845,6 +1042,7 @@
         @Override
         public void onUserSwitchComplete(int userId) {
             if (mVisible) {
+                updateOwnerInfo();
                 updateIndication(false);
             }
         }
@@ -857,11 +1055,39 @@
         }
 
         @Override
+        public void onLogoutEnabledChanged() {
+            if (mVisible) {
+                updateLogoutView();
+            }
+        }
+
+        @Override
         public void onRequireUnlockForNfc() {
             showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc),
                     false /* isError */, false /* hideOnScreenOff */);
             hideTransientIndicationDelayed(HIDE_DELAY_MS);
         }
-
     }
+
+    private StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+        @Override
+        public void onStateChanged(int newState) {
+            setVisible(newState == StatusBarState.KEYGUARD);
+        }
+
+        @Override
+        public void onDozingChanged(boolean dozing) {
+            if (mDozing == dozing) {
+                return;
+            }
+            mDozing = dozing;
+
+            if (mHideTransientMessageOnScreenOff && mDozing) {
+                hideTransientIndication();
+            } else {
+                updateIndication(false);
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 2f0f90d..c1feaca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,14 +11,10 @@
 import android.graphics.PorterDuffXfermode
 import android.graphics.RadialGradient
 import android.graphics.Shader
-import android.os.SystemProperties
 import android.util.AttributeSet
 import android.view.View
 import com.android.systemui.Interpolators
 
-val enableLightReveal =
-        SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false)
-
 /**
  * Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to
  * 100% of the view(s) underneath the scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index a816ecc..cfadcd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -388,7 +388,28 @@
      */
     public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
             PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
+        return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+                null /* userMessageContent */, null /* authBypassCheck */);
+    }
 
+    /**
+     * Activates a given {@link RemoteInput}
+     *
+     * @param view The view of the action button or suggestion chip that was tapped.
+     * @param inputs The remote inputs that need to be sent to the app.
+     * @param input The remote input that needs to be activated.
+     * @param pendingIntent The pending intent to be sent to the app.
+     * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+     *         {@code null} if the user is not editing a smart reply.
+     * @param userMessageContent User-entered text with which to initialize the remote input view.
+     * @param authBypassCheck Optional auth bypass check associated with this remote input
+     *         activation. If {@code null}, we never bypass.
+     * @return Whether the {@link RemoteInput} was activated.
+     */
+    public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+            PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+            @Nullable String userMessageContent,
+            @Nullable AuthBypassPredicate authBypassCheck) {
         ViewParent p = view.getParent();
         RemoteInputView riv = null;
         ExpandableNotificationRow row = null;
@@ -410,40 +431,9 @@
 
         row.setUserExpanded(true);
 
-        if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
-            final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-
-            final boolean isLockedManagedProfile =
-                    mUserManager.getUserInfo(userId).isManagedProfile()
-                    && mKeyguardManager.isDeviceLocked(userId);
-
-            final boolean isParentUserLocked;
-            if (isLockedManagedProfile) {
-                final UserInfo profileParent = mUserManager.getProfileParent(userId);
-                isParentUserLocked = (profileParent != null)
-                        && mKeyguardManager.isDeviceLocked(profileParent.id);
-            } else {
-                isParentUserLocked = false;
-            }
-
-            if (mLockscreenUserManager.isLockscreenPublicMode(userId)
-                    || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
-                // If the parent user is no longer locked, and the user to which the remote input
-                // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be
-                // called to unlock it.
-                if (isLockedManagedProfile && !isParentUserLocked) {
-                    mCallback.onLockedWorkRemoteInput(userId, row, view);
-                } else {
-                    // Even if we don't have security we should go through this flow, otherwise
-                    // we won't go to the shade.
-                    mCallback.onLockedRemoteInput(row, view);
-                }
-                return true;
-            }
-            if (isLockedManagedProfile) {
-                mCallback.onLockedWorkRemoteInput(userId, row, view);
-                return true;
-            }
+        final boolean deferBouncer = authBypassCheck != null;
+        if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+            return true;
         }
 
         if (riv != null && !riv.isAttachedToWindow()) {
@@ -461,7 +451,10 @@
                 && !row.getPrivateLayout().getExpandedChild().isShown()) {
             // The expanded layout is selected, but it's not shown yet, let's wait on it to
             // show before we do the animation.
-            mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+            mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+                activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+                        userMessageContent, authBypassCheck);
+            });
             return true;
         }
 
@@ -491,10 +484,62 @@
         riv.setPendingIntent(pendingIntent);
         riv.setRemoteInput(inputs, input, editedSuggestionInfo);
         riv.focusAnimated();
+        if (userMessageContent != null) {
+            riv.setEditTextContent(userMessageContent);
+        }
+        if (deferBouncer) {
+            final ExpandableNotificationRow finalRow = row;
+            riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer()
+                    && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+        }
 
         return true;
     }
 
+    private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
+            ExpandableNotificationRow row) {
+        if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+            return false;
+        }
+
+        final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+
+        final boolean isLockedManagedProfile =
+                mUserManager.getUserInfo(userId).isManagedProfile()
+                        && mKeyguardManager.isDeviceLocked(userId);
+
+        final boolean isParentUserLocked;
+        if (isLockedManagedProfile) {
+            final UserInfo profileParent = mUserManager.getProfileParent(userId);
+            isParentUserLocked = (profileParent != null)
+                    && mKeyguardManager.isDeviceLocked(profileParent.id);
+        } else {
+            isParentUserLocked = false;
+        }
+
+        if ((mLockscreenUserManager.isLockscreenPublicMode(userId)
+                || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) {
+            // If the parent user is no longer locked, and the user to which the remote
+            // input
+            // is destined is a locked, managed profile, then onLockedWorkRemoteInput
+            // should be
+            // called to unlock it.
+            if (isLockedManagedProfile && !isParentUserLocked) {
+                mCallback.onLockedWorkRemoteInput(userId, row, view);
+            } else {
+                // Even if we don't have security we should go through this flow, otherwise
+                // we won't go to the shade.
+                mCallback.onLockedRemoteInput(row, view);
+            }
+            return true;
+        }
+        if (isLockedManagedProfile) {
+            mCallback.onLockedWorkRemoteInput(userId, row, view);
+            return true;
+        }
+        return false;
+    }
+
     private RemoteInputView findRemoteInputView(View v) {
         if (v == null) {
             return null;
@@ -807,8 +852,11 @@
          *
          * @param row
          * @param clickedView
+         * @param deferBouncer
+         * @param runnable
          */
-        void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView);
+        void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView,
+                boolean deferBouncer, Runnable runnable);
 
         /**
          * Return whether or not remote input should be handled for this view.
@@ -845,4 +893,28 @@
          */
         boolean handleClick();
     }
+
+    /**
+     * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[],
+     * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)}
+     * invocation that determines whether or not the bouncer can be bypassed when sending the
+     * RemoteInput.
+     */
+    public interface AuthBypassPredicate {
+        /**
+         * Determines if the RemoteInput can be sent without the bouncer. Should be checked the
+         * same frame that the RemoteInput is to be sent.
+         */
+        boolean canSendRemoteInputWithoutBouncer();
+    }
+
+    /** Shows the bouncer if necessary */
+    public interface BouncerChecker {
+        /**
+         * Shows the bouncer if necessary in order to send a RemoteInput.
+         *
+         * @return {@code true} if the bouncer was shown, {@code false} otherwise
+         */
+        boolean showBouncerIfNecessary();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
+import android.view.WindowManager;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
         }
 
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] remoteAnimationTargets,
                 RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+                RemoteAnimationTarget[] remoteAnimationNonAppTargets,
                 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
                     throws RemoteException {
             mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a03fc13..845d321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -71,6 +71,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.CallLayout;
 import com.android.internal.widget.MessagingLayout;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
@@ -165,6 +166,7 @@
     private int mMaxSmallHeightLarge;
     private int mMaxSmallHeightMedia;
     private int mMaxExpandedHeight;
+    private int mMaxCallHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
     private boolean mMustStayOnScreen;
@@ -645,8 +647,9 @@
     }
 
     private void updateLimitsForView(NotificationContentView layout) {
-        boolean customView = layout.getContractedChild() != null
-                && layout.getContractedChild().getId()
+        View contractedView = layout.getContractedChild();
+        boolean customView = contractedView != null
+                && contractedView.getId()
                 != com.android.internal.R.id.status_bar_latest_event_content;
         boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
         boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
@@ -661,7 +664,8 @@
         View expandedView = layout.getExpandedChild();
         boolean isMediaLayout = expandedView != null
                 && expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
-        boolean isMessagingLayout = layout.getContractedChild() instanceof MessagingLayout;
+        boolean isMessagingLayout = contractedView instanceof MessagingLayout;
+        boolean isCallLayout = contractedView instanceof CallLayout;
         boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
 
         if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -684,6 +688,8 @@
             //  make sure we don't crop them terribly.  We actually need to revisit this and give
             //  them a headerless design, then remove this hack.
             smallHeight = mMaxSmallHeightLarge;
+        } else if (isCallLayout) {
+            smallHeight = mMaxCallHeight;
         } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
             smallHeight = mMaxSmallHeightLarge;
         } else {
@@ -1645,6 +1651,8 @@
                 R.dimen.notification_min_height_media);
         mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
+        mMaxCallHeight = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.call_notification_full_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
new file mode 100644
index 0000000..4541ebf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.systemui.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import com.android.internal.widget.CachingIconView
+import com.android.internal.widget.CallLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Wraps a notification containing a call template
+ */
+class NotificationCallTemplateViewWrapper constructor(
+    ctx: Context,
+    view: View,
+    row: ExpandableNotificationRow
+) : NotificationTemplateViewWrapper(ctx, view, row) {
+
+    private val minHeightWithActions: Int =
+            NotificationUtils.getFontScaledHeight(ctx, R.dimen.call_notification_full_height)
+    private val callLayout: CallLayout = view as CallLayout
+
+    private lateinit var conversationIconView: CachingIconView
+    private lateinit var conversationBadgeBg: View
+    private lateinit var expandBtn: View
+    private lateinit var appName: View
+    private lateinit var conversationTitleView: View
+
+    private fun resolveViews() {
+        with(callLayout) {
+            conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
+            conversationBadgeBg =
+                    requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
+            expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+            appName = requireViewById(com.android.internal.R.id.app_name_text)
+            conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+        }
+    }
+
+    override fun onContentUpdated(row: ExpandableNotificationRow) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews()
+        super.onContentUpdated(row)
+    }
+
+    override fun updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes()
+        addTransformedViews(
+                appName,
+                conversationTitleView
+        )
+        addViewsTransformingToSimilar(
+                conversationIconView,
+                conversationBadgeBg,
+                expandBtn
+        )
+    }
+
+    override fun disallowSingleClick(x: Float, y: Float): Boolean {
+        val isOnExpandButton = expandBtn.visibility == View.VISIBLE &&
+                isOnView(expandBtn, x, y)
+        return isOnExpandButton || super.disallowSingleClick(x, y)
+    }
+
+    override fun getMinLayoutHeight(): Int = minHeightWithActions
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index c49f6cb..905bccf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.view.View
-import android.view.View.GONE
 import android.view.ViewGroup
 import com.android.internal.widget.CachingIconView
 import com.android.internal.widget.ConversationLayout
@@ -48,8 +47,8 @@
 
     private lateinit var conversationIconView: CachingIconView
     private lateinit var conversationBadgeBg: View
-    private lateinit var expandButton: View
-    private lateinit var expandButtonContainer: View
+    private lateinit var expandBtn: View
+    private lateinit var expandBtnContainer: View
     private lateinit var imageMessageContainer: ViewGroup
     private lateinit var messagingLinearLayout: MessagingLinearLayout
     private lateinit var conversationTitleView: View
@@ -66,9 +65,8 @@
             conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
             conversationBadgeBg =
                     requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
-            expandButton = requireViewById(com.android.internal.R.id.expand_button)
-            expandButtonContainer =
-                    requireViewById(com.android.internal.R.id.expand_button_container)
+            expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+            expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
             importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
             appName = requireViewById(com.android.internal.R.id.app_name_text)
             conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
@@ -126,7 +124,7 @@
         addViewsTransformingToSimilar(
                 conversationIconView,
                 conversationBadgeBg,
-                expandButton,
+                expandBtn,
                 importanceRing,
                 facePileTop,
                 facePileBottom,
@@ -134,11 +132,9 @@
         )
     }
 
-    override fun getExpandButton() = super.getExpandButton()
-
     override fun setShelfIconVisible(visible: Boolean) {
         if (conversationLayout.isImportantConversation) {
-            if (conversationIconView.visibility != GONE) {
+            if (conversationIconView.visibility != View.GONE) {
                 conversationIconView.isForceHidden = visible
                 // We don't want the small icon to be hidden by the extended wrapper, as force
                 // hiding the conversationIcon will already do that via its listener.
@@ -152,7 +148,7 @@
 
     override fun getShelfTransformationTarget(): View? =
             if (conversationLayout.isImportantConversation)
-                if (conversationIconView.visibility != GONE)
+                if (conversationIconView.visibility != View.GONE)
                     conversationIconView
                 else
                     // A notification with a fallback icon was set to important. Currently
@@ -169,8 +165,8 @@
             conversationLayout.updateExpandability(expandable, onClickListener)
 
     override fun disallowSingleClick(x: Float, y: Float): Boolean {
-        val isOnExpandButton = expandButtonContainer.visibility == View.VISIBLE &&
-                isOnView(expandButtonContainer, x, y)
+        val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
+                isOnView(expandBtnContainer, x, y)
         return isOnExpandButton || super.disallowSingleClick(x, y)
     }
 
@@ -179,10 +175,4 @@
                 minHeightWithActions
             else
                 super.getMinLayoutHeight()
-
-    private fun addTransformedViews(vararg vs: View?) =
-            vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) }
-
-    private fun addViewsTransformingToSimilar(vararg vs: View?) =
-            vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 7964845..301c372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -29,6 +29,30 @@
 
     private View mWrappedView = null;
 
+    /**
+     * Determines if the standard template contains a custom view, injected by Notification.Builder
+     */
+    public static boolean hasCustomView(View v) {
+        return getWrappedCustomView(v) != null;
+    }
+
+    private static View getWrappedCustomView(View view) {
+        if (view == null) {
+            return null;
+        }
+        ViewGroup container = view.findViewById(
+                com.android.internal.R.id.notification_main_column);
+        if (container == null) {
+            return null;
+        }
+        Integer childIndex = (Integer) container.getTag(
+                com.android.internal.R.id.notification_custom_view_index_tag);
+        if (childIndex == null || childIndex == -1) {
+            return null;
+        }
+        return container.getChildAt(childIndex);
+    }
+
     protected NotificationDecoratedCustomViewWrapper(Context ctx, View view,
             ExpandableNotificationRow row) {
         super(ctx, view, row);
@@ -36,13 +60,7 @@
 
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
-        ViewGroup container = mView.findViewById(
-                com.android.internal.R.id.notification_main_column);
-        Integer childIndex = (Integer) container.getTag(
-                com.android.internal.R.id.notification_custom_view_index_tag);
-        if (childIndex != null && childIndex != -1) {
-            mWrappedView = container.getChildAt(childIndex);
-        }
+        mWrappedView = getWrappedCustomView(mView);
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 97201f5..34bc537 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -36,7 +36,6 @@
 
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.NotificationExpandButton;
-import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.ViewTransformationHelper;
@@ -60,6 +59,7 @@
     private CachingIconView mIcon;
     private NotificationExpandButton mExpandButton;
     private View mAltExpandTarget;
+    private View mIconContainer;
     protected NotificationHeaderView mNotificationHeader;
     protected NotificationTopLineView mNotificationTopLine;
     private TextView mHeaderText;
@@ -112,6 +112,7 @@
         mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
         mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target);
+        mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container);
         mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
         mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
@@ -203,11 +204,8 @@
     public void clearConversationSkin() {
         if (mAppNameText != null) {
             final ColorStateList colors = mAppNameText.getTextColors();
-            final int textAppearance = Utils.getThemeAttr(
-                    mAppNameText.getContext(),
-                    com.android.internal.R.attr.notificationHeaderTextAppearance,
+            mAppNameText.setTextAppearance(
                     com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
-            mAppNameText.setTextAppearance(textAppearance);
             mAppNameText.setTextColor(colors);
             MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
             final int marginStart = mAppNameText.getResources().getDimensionPixelSize(
@@ -265,19 +263,11 @@
         mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
         mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER,
                 mExpandButton);
-        if (mWorkProfileImage != null) {
-            mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage);
-        }
         if (mIsLowPriority && mHeaderText != null) {
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
                     mHeaderText);
         }
-        if (mAudiblyAlertedIcon != null) {
-            mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
-        }
-        if (mFeedbackIcon != null) {
-            mTransformationHelper.addViewTransformingToSimilar(mFeedbackIcon);
-        }
+        addViewsTransformingToSimilar(mWorkProfileImage, mAudiblyAlertedIcon, mFeedbackIcon);
     }
 
     @Override
@@ -287,6 +277,9 @@
         if (mAltExpandTarget != null) {
             mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null);
         }
+        if (mIconContainer != null) {
+            mIconContainer.setOnClickListener(expandable ? onClickListener : null);
+        }
         if (mNotificationHeader != null) {
             mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
         }
@@ -371,4 +364,20 @@
         super.setVisible(visible);
         mTransformationHelper.setVisible(visible);
     }
+
+    protected void addTransformedViews(View... views) {
+        for (View view : views) {
+            if (view != null) {
+                mTransformationHelper.addTransformedView(view);
+            }
+        }
+    }
+
+    protected void addViewsTransformingToSimilar(View... views) {
+        for (View view : views) {
+            if (view != null) {
+                mTransformationHelper.addViewTransformingToSimilar(view);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 7b5c5f6..5fff8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -74,12 +74,17 @@
                 return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
             } else if ("conversation".equals(v.getTag())) {
                 return new NotificationConversationTemplateViewWrapper(ctx, v, row);
+            } else if ("call".equals(v.getTag())) {
+                return new NotificationCallTemplateViewWrapper(ctx, v, row);
             }
             Class<? extends Notification.Style> style =
                     row.getEntry().getSbn().getNotification().getNotificationStyle();
             if (Notification.DecoratedCustomViewStyle.class.equals(style)) {
                 return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
             }
+            if (NotificationDecoratedCustomViewWrapper.hasCustomView(v)) {
+                return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
+            }
             return new NotificationTemplateViewWrapper(ctx, v, row);
         } else if (v instanceof NotificationHeaderView) {
             return new NotificationHeaderViewWrapper(ctx, v, row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e6efba7..5d2203b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -286,8 +286,7 @@
         float currentYPosition = -algorithmState.scrollY;
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
-                    false /* reverse */);
+            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
         }
     }
 
@@ -301,10 +300,6 @@
      * @param currentYPosition The Y position of the current pass of the algorithm.  For a forward
      *                         pass, this should be the top of the child; for a reverse pass, the
      *                         bottom of the child.
-     * @param reverse          Whether we're laying out children in the reverse direction (Y
-     *                         positions
-     *                         decreasing) instead of the forward direction (Y positions
-     *                         increasing).
      * @return The Y position after laying out the child.  This will be the {@code currentYPosition}
      * for the next call to this method, after adjusting for any gaps between children.
      */
@@ -312,8 +307,7 @@
             int i,
             StackScrollAlgorithmState algorithmState,
             AmbientState ambientState,
-            float currentYPosition,
-            boolean reverse) {
+            float currentYPosition) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
         ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
         final boolean applyGapHeight =
@@ -323,20 +317,12 @@
         ExpandableViewState childViewState = child.getViewState();
         childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
 
-        if (applyGapHeight && !reverse) {
+        if (applyGapHeight) {
             currentYPosition += mGapHeight;
         }
-
         int childHeight = getMaxAllowedChildHeight(child);
-        if (reverse) {
-            childViewState.yTranslation = currentYPosition
-                    - (childHeight + mPaddingBetweenElements);
-            if (currentYPosition <= 0) {
-                childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
-            }
-        } else {
-            childViewState.yTranslation = currentYPosition;
-        }
+        childViewState.yTranslation = currentYPosition;
+
         boolean isFooterView = child instanceof FooterView;
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
@@ -362,16 +348,9 @@
             clampPositionToShelf(child, childViewState, ambientState);
         }
 
-        if (reverse) {
-            currentYPosition = childViewState.yTranslation;
-            if (applyGapHeight) {
-                currentYPosition -= mGapHeight;
-            }
-        } else {
-            currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
-            if (currentYPosition <= 0) {
-                childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
-            }
+        currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+        if (currentYPosition <= 0) {
+            childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
         }
         if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
             Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 8c2fa33..85d8df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -54,6 +55,7 @@
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
     private final Resources mResources;
     private final BatteryController mBatteryController;
+    private final FeatureFlags mFeatureFlags;
 
     private boolean mDozeAlwaysOn;
     private boolean mControlScreenOffAnimation;
@@ -65,7 +67,8 @@
             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
             PowerManager powerManager,
             BatteryController batteryController,
-            TunerService tunerService) {
+            TunerService tunerService,
+            FeatureFlags featureFlags) {
         mResources = resources;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -74,6 +77,7 @@
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = powerManager;
         mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+        mFeatureFlags = featureFlags;
 
         tunerService.addTunable(
                 this,
@@ -200,8 +204,7 @@
      * then abruptly showing AOD.
      */
     public boolean shouldControlUnlockedScreenOff() {
-        return getAlwaysOn() && SystemProperties.getBoolean(
-                "persist.sysui.show_new_screen_on_transitions", false);
+        return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0a366c9..2ce0a87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -48,6 +48,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.service.media.CameraPrewarmService;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
@@ -60,6 +61,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,10 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.controls.dagger.ControlsComponent;
+import com.android.systemui.controls.ui.ControlsDialog;
+import com.android.systemui.controls.ui.ControlsUiController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.IntentButtonProvider;
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -117,14 +123,16 @@
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
 
+    // TODO(b/179494051): May no longer be needed
     private final boolean mShowLeftAffordance;
     private final boolean mShowCameraAffordance;
 
     private KeyguardAffordanceView mRightAffordanceView;
     private KeyguardAffordanceView mLeftAffordanceView;
+    private ImageView mAltLeftButton;
     private ViewGroup mIndicationArea;
-    private TextView mEnterpriseDisclosure;
     private TextView mIndicationText;
+    private TextView mIndicationTextBottom;
     private ViewGroup mPreviewContainer;
     private ViewGroup mOverlayContainer;
 
@@ -171,6 +179,11 @@
     private int mBurnInYOffset;
     private ActivityIntentHelper mActivityIntentHelper;
 
+    private ControlsDialog mControlsDialog;
+    private ControlsComponent mControlsComponent;
+    private int mLockScreenMode;
+    private BroadcastDispatcher mBroadcastDispatcher;
+
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
     }
@@ -236,10 +249,10 @@
         mOverlayContainer = findViewById(R.id.overlay_container);
         mRightAffordanceView = findViewById(R.id.camera_button);
         mLeftAffordanceView = findViewById(R.id.left_button);
+        mAltLeftButton = findViewById(R.id.alt_left_button);
         mIndicationArea = findViewById(R.id.keyguard_indication_area);
-        mEnterpriseDisclosure = findViewById(
-                R.id.keyguard_indication_enterprise_disclosure);
         mIndicationText = findViewById(R.id.keyguard_indication_text);
+        mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom);
         mBurnInYOffset = getResources().getDimensionPixelSize(
@@ -316,7 +329,7 @@
         }
 
         // Respect font size setting.
-        mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+        mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                 getResources().getDimensionPixelSize(
                         com.android.internal.R.dimen.text_size_small_material));
         mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -334,6 +347,11 @@
         lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
         mLeftAffordanceView.setLayoutParams(lp);
         updateLeftAffordanceIcon();
+
+        lp = mAltLeftButton.getLayoutParams();
+        lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
+        lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
+        mAltLeftButton.setLayoutParams(lp);
     }
 
     private void updateRightAffordanceIcon() {
@@ -392,10 +410,17 @@
     }
 
     private void updateLeftAffordanceIcon() {
+        if (mDozing) {
+            mAltLeftButton.setVisibility(GONE);
+        } else if (mAltLeftButton.getDrawable() != null) {
+            mAltLeftButton.setVisibility(VISIBLE);
+        }
+
         if (!mShowLeftAffordance || mDozing) {
             mLeftAffordanceView.setVisibility(GONE);
             return;
         }
+
         IconState state = mLeftButton.getIcon();
         mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
         if (state.drawable != mLeftAffordanceView.getDrawable()
@@ -669,6 +694,9 @@
 
     public void startFinishDozeAnimation() {
         long delay = 0;
+        if (mAltLeftButton.getVisibility() == View.VISIBLE) {
+            startFinishDozeAnimationElement(mAltLeftButton, delay);
+        }
         if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
             startFinishDozeAnimationElement(mLeftAffordanceView, delay);
             delay += DOZE_ANIMATION_STAGGER_DELAY;
@@ -744,6 +772,10 @@
 
         if (dozing) {
             mOverlayContainer.setVisibility(INVISIBLE);
+            if (mControlsDialog != null) {
+                mControlsDialog.dismiss();
+                mControlsDialog = null;
+            }
         } else {
             mOverlayContainer.setVisibility(VISIBLE);
             if (animate) {
@@ -773,6 +805,7 @@
         mLeftAffordanceView.setAlpha(alpha);
         mRightAffordanceView.setAlpha(alpha);
         mIndicationArea.setAlpha(alpha);
+        mAltLeftButton.setAlpha(alpha);
     }
 
     private class DefaultLeftButton implements IntentButton {
@@ -844,4 +877,54 @@
         }
         return insets;
     }
+
+    /**
+     * Show or hide controls, depending on the lock screen mode and controls
+     * availability.
+     */
+    public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) {
+        mControlsComponent = component;
+        mBroadcastDispatcher = dispatcher;
+        setupControls();
+    }
+
+    private void setupControls() {
+        if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            mAltLeftButton.setVisibility(View.GONE);
+            mAltLeftButton.setOnClickListener(null);
+            return;
+        }
+
+        if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
+            return;
+        }
+
+        if (mControlsComponent.getControlsListingController().isPresent()) {
+            mControlsComponent.getControlsListingController().get()
+                    .addCallback(list -> {
+                        if (!list.isEmpty()) {
+                            mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
+                            mAltLeftButton.setVisibility(View.VISIBLE);
+                            mAltLeftButton.setOnClickListener((v) -> {
+                                ControlsUiController ui = mControlsComponent
+                                        .getControlsUiController().get();
+                                mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
+                                        .show(ui);
+                            });
+
+                        } else {
+                            mAltLeftButton.setVisibility(View.GONE);
+                            mAltLeftButton.setOnClickListener(null);
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Optionally add controls when in the new lockscreen mode
+     */
+    public void onLockScreenModeChanged(int mode) {
+        mLockScreenMode = mode;
+        setupControls();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 30c951a..8970a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -16,11 +16,15 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -28,6 +32,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Interpolators;
+import com.android.systemui.keyguard.KeyguardIndication;
 
 import java.util.LinkedList;
 
@@ -35,13 +40,15 @@
  * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
  */
 public class KeyguardIndicationTextView extends TextView {
-
     private static final int FADE_OUT_MILLIS = 200;
     private static final int FADE_IN_MILLIS = 250;
     private static final long MSG_DURATION_MILLIS = 600;
     private long mNextAnimationTime = 0;
     private boolean mAnimationsEnabled = true;
     private LinkedList<CharSequence> mMessages = new LinkedList<>();
+    private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>();
+
+    private boolean mUseNewAnimations = false;
 
     public KeyguardIndicationTextView(Context context) {
         super(context);
@@ -60,12 +67,33 @@
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    public void setLockScreenMode(int lockScreenMode) {
+        mUseNewAnimations = lockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1;
+    }
+
+    /**
+     * Changes the text with an animation and makes sure a single indication is shown long enough.
+     */
+    public void switchIndication(int textResId) {
+        switchIndication(getResources().getText(textResId), null);
+    }
+
+    /**
+     * Changes the text with an animation and makes sure a single indication is shown long enough.
+     *
+     * @param indication The text to show.
+     */
+    public void switchIndication(KeyguardIndication indication) {
+        switchIndication(indication == null ? null : indication.getMessage(), indication);
+    }
+
     /**
      * Changes the text with an animation and makes sure a single indication is shown long enough.
      *
      * @param text The text to show.
+     * @param indication optional display information for the text
      */
-    public void switchIndication(CharSequence text) {
+    public void switchIndication(CharSequence text, KeyguardIndication indication) {
         if (text == null) text = "";
 
         CharSequence lastPendingMessage = mMessages.peekLast();
@@ -74,55 +102,119 @@
             return;
         }
         mMessages.add(text);
+        mKeyguardIndicationInfo.add(indication);
 
-        Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
-        fadeOut.setDuration(getFadeOutMillis());
-        fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-
-        final CharSequence nextText = text;
-        fadeOut.addListener(new AnimatorListenerAdapter() {
-                public void onAnimationEnd(Animator animator) {
-                    setText(mMessages.poll());
-                }
-            });
-
+        final boolean hasIcon = indication != null && indication.getIcon() != null;
         final AnimatorSet animSet = new AnimatorSet();
-        final AnimatorSet.Builder animSetBuilder = animSet.play(fadeOut);
+        final AnimatorSet.Builder animSetBuilder = animSet.play(getOutAnimator());
 
         // Make sure each animation is visible for a minimum amount of time, while not worrying
         // about fading in blank text
         long timeInMillis = System.currentTimeMillis();
         long delay = Math.max(0, mNextAnimationTime - timeInMillis);
-        setNextAnimationTime(timeInMillis + delay + getFadeOutMillis());
+        setNextAnimationTime(timeInMillis + delay + getFadeOutDuration());
 
-        if (!text.equals("")) {
+        if (!text.equals("") || hasIcon) {
             setNextAnimationTime(mNextAnimationTime + MSG_DURATION_MILLIS);
-
-            ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f);
-            fadeIn.setDuration(getFadeInMillis());
-            fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            animSetBuilder.before(fadeIn);
+            animSetBuilder.before(getInAnimator());
         }
 
         animSet.setStartDelay(delay);
         animSet.start();
     }
 
+    private AnimatorSet getOutAnimator() {
+        AnimatorSet animatorSet = new AnimatorSet();
+        Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
+        fadeOut.setDuration(getFadeOutDuration());
+        fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+        fadeOut.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                KeyguardIndication info = mKeyguardIndicationInfo.poll();
+                if (info != null) {
+                    setTextColor(info.getTextColor());
+                    setOnClickListener(info.getClickListener());
+                    final Drawable icon = info.getIcon();
+                    if (icon != null) {
+                        icon.setTint(getCurrentTextColor());
+                        if (icon instanceof AnimatedVectorDrawable) {
+                            ((AnimatedVectorDrawable) icon).start();
+                        }
+                    }
+                    setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
+                }
+                setText(mMessages.poll());
+            }
+        });
+
+        if (mUseNewAnimations) {
+            Animator yTranslate =
+                    ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels());
+            yTranslate.setDuration(getFadeOutDuration());
+            fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+            animatorSet.playTogether(fadeOut, yTranslate);
+        } else {
+            animatorSet.play(fadeOut);
+        }
+
+        return animatorSet;
+    }
+
+    private AnimatorSet getInAnimator() {
+        AnimatorSet animatorSet = new AnimatorSet();
+        ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f);
+        fadeIn.setStartDelay(getFadeInDelay());
+        fadeIn.setDuration(getFadeInDuration());
+        fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+
+        if (mUseNewAnimations) {
+            Animator yTranslate =
+                    ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getYTranslationPixels(), 0);
+            yTranslate.setDuration(getYInDuration());
+            yTranslate.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    setTranslationY(0);
+                }
+            });
+            animatorSet.playTogether(yTranslate, fadeIn);
+        } else {
+            animatorSet.play(fadeIn);
+        }
+
+        return animatorSet;
+    }
+
     @VisibleForTesting
     public void setAnimationsEnabled(boolean enabled) {
         mAnimationsEnabled = enabled;
     }
 
-    private long getFadeInMillis() {
-        if (mAnimationsEnabled) return FADE_IN_MILLIS;
+    private long getFadeInDelay() {
+        if (!mAnimationsEnabled) return 0L;
+        if (mUseNewAnimations) return 150L;
         return 0L;
     }
 
-    private long getFadeOutMillis() {
-        if (mAnimationsEnabled) return FADE_OUT_MILLIS;
+    private long getFadeInDuration() {
+        if (!mAnimationsEnabled) return 0L;
+        if (mUseNewAnimations) return 317L;
+        return FADE_IN_MILLIS;
+    }
+
+    private long getYInDuration() {
+        if (!mAnimationsEnabled) return 0L;
+        if (mUseNewAnimations) return 600L;
         return 0L;
     }
 
+    private long getFadeOutDuration() {
+        if (!mAnimationsEnabled) return 0L;
+        if (mUseNewAnimations) return 167L;
+        return FADE_OUT_MILLIS;
+    }
+
     private void setNextAnimationTime(long time) {
         if (mAnimationsEnabled) {
             mNextAnimationTime = time;
@@ -131,10 +223,8 @@
         }
     }
 
-    /**
-     * See {@link #switchIndication}.
-     */
-    public void switchIndication(int textResId) {
-        switchIndication(getResources().getText(textResId));
+    private int getYTranslationPixels() {
+        return mContext.getResources().getDimensionPixelSize(
+                com.android.systemui.R.dimen.keyguard_indication_y_translation);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3b47594..e0ef3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -80,8 +80,10 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.controls.dagger.ControlsComponent;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeLog;
@@ -229,6 +231,7 @@
                 public void onLockScreenModeChanged(int mode) {
                     mLockScreenMode = mode;
                     mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+                    mKeyguardBottomArea.onLockScreenModeChanged(mode);
                 }
 
                 @Override
@@ -295,6 +298,8 @@
     private final QSDetailDisplayer mQSDetailDisplayer;
     private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
+    private final ControlsComponent mControlsComponent;
+
     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
     private final int mMaxKeyguardNotifications;
@@ -503,6 +508,7 @@
     private NotificationShelfController mNotificationShelfController;
 
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+    private BroadcastDispatcher mBroadcastDispatcher;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
@@ -557,8 +563,9 @@
             ScrimController scrimController,
             MediaDataManager mediaDataManager,
             AmbientState ambientState,
-            FeatureFlags featureFlags
-    ) {
+            FeatureFlags featureFlags,
+            ControlsComponent controlsComponent,
+            BroadcastDispatcher broadcastDispatcher) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -592,6 +599,7 @@
         mBiometricUnlockController = biometricUnlockController;
         mScrimController = scrimController;
         mMediaDataManager = mediaDataManager;
+        mControlsComponent = controlsComponent;
         pulseExpansionHandler.setPulseExpandAbortListener(() -> {
             if (mQs != null) {
                 mQs.animateHeaderSlidingOut();
@@ -630,6 +638,7 @@
         mEntryManager = notificationEntryManager;
         mConversationNotificationManager = conversationNotificationManager;
         mAuthController = authController;
+        mBroadcastDispatcher = broadcastDispatcher;
 
         mView.setBackgroundColor(Color.TRANSPARENT);
         OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -838,6 +847,7 @@
         mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
         mKeyguardBottomArea.setStatusBar(mStatusBar);
         mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
+        mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher);
     }
 
     private void updateMaxDisplayedNotifications(boolean recompute) {
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 7095afd..1e19bee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -35,7 +35,6 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -181,6 +180,7 @@
 import com.android.systemui.statusbar.BackDropView;
 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;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -442,6 +442,7 @@
     private final KeyguardViewMediator mKeyguardViewMediator;
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
+    private final FeatureFlags mFeatureFlags;
 
     private final List<ExpansionChangedListener> mExpansionChangedListeners;
 
@@ -765,7 +766,8 @@
             Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory) {
+            BrightnessSlider.Factory brightnessSliderFactory,
+            FeatureFlags featureFlags) {
         super(context);
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
@@ -843,6 +845,7 @@
         mDemoModeController = demoModeController;
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
+        mFeatureFlags = featureFlags;
 
         mExpansionChangedListeners = new ArrayList<>();
 
@@ -1143,8 +1146,6 @@
             mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
         }
 
-        mKeyguardIndicationController.setIndicationArea(
-                mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area));
         mNotificationPanelViewController.setKeyguardIndicationController(
                 mKeyguardIndicationController);
 
@@ -1186,9 +1187,11 @@
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
 
-        if (getEnableLightReveal()) {
+        if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) {
             mLightRevealScrim.setVisibility(View.VISIBLE);
             mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+        } else {
+            mLightRevealScrim.setVisibility(View.GONE);
         }
 
         mNotificationPanelViewController.initDependencies(
@@ -3620,26 +3623,19 @@
         mNavigationBarController.touchAutoDim(mDisplayId);
         Trace.beginSection("StatusBar#updateKeyguardState");
         if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardIndicationController.setVisible(true);
             if (mKeyguardUserSwitcher != null) {
                 mKeyguardUserSwitcher.setKeyguard(true,
                         mStatusBarStateController.fromShadeLocked());
             }
             if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
-            if (mAmbientIndicationContainer != null) {
-                mAmbientIndicationContainer.setVisibility(View.VISIBLE);
-            }
         } else {
-            mKeyguardIndicationController.setVisible(false);
             if (mKeyguardUserSwitcher != null) {
                 mKeyguardUserSwitcher.setKeyguard(false,
                         mStatusBarStateController.goingToFullShade() ||
                                 mState == StatusBarState.SHADE_LOCKED ||
                                 mStatusBarStateController.fromShadeLocked());
             }
-            if (mAmbientIndicationContainer != null) {
-                mAmbientIndicationContainer.setVisibility(View.INVISIBLE);
-            }
+
         }
         updateDozingState();
         checkBarModes();
@@ -3651,7 +3647,7 @@
 
     @Override
     public void onDozeAmountChanged(float linear, float eased) {
-        if (getEnableLightReveal()) {
+        if (mFeatureFlags.useNewLockscreenAnimations()) {
             mLightRevealScrim.setRevealAmount(1f - linear);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 36519ac..983b296e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -180,8 +180,8 @@
 
     @Override
     public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
-            View clickedView) {
-        if (mKeyguardStateController.isShowing()) {
+            View clickedView, boolean deferBouncer, Runnable runnable) {
+        if (!deferBouncer && mKeyguardStateController.isShowing()) {
             onLockedRemoteInput(row, clickedView);
         } else {
             if (row.isChildInGroup() && !row.areChildrenExpanded()) {
@@ -189,7 +189,7 @@
                 mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
             }
             row.setUserExpanded(true);
-            row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
+            row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 9e9533d..b572c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -47,6 +47,7 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,8 @@
             DismissCallbackRegistry dismissCallbackRegistry,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory) {
+            BrightnessSlider.Factory brightnessSliderFactory,
+            FeatureFlags featureFlags) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -279,6 +281,7 @@
                 notificationShadeDepthController,
                 statusBarTouchableRegionManager,
                 notificationIconAreaController,
-                brightnessSliderFactory);
+                brightnessSliderFactory,
+                featureFlags);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 08a4492..ccaa1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -25,6 +25,8 @@
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,11 +44,18 @@
     private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5;
     private static final int MSG_ADD_REMOVE_EMERGENCY        = 6;
     private static final int MSG_ADD_REMOVE_SIGNAL           = 7;
+    private static final int HISTORY_SIZE = 64;
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     // All the callbacks.
     private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>();
     private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mHistoryIndex;
+
     public CallbackHandler() {
         super(Looper.getMainLooper());
     }
@@ -111,12 +120,27 @@
     public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
             final IconState qsIcon, final boolean activityIn, final boolean activityOut,
             final String description, boolean isTransient, String secondaryLabel) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setWifiIndicators: ")
+                .append("enabled=").append(enabled).append(",")
+                .append("statusIcon=").append(statusIcon).append(",")
+                .append("qsIcon=").append(qsIcon).append(",")
+                .append("activityIn=").append(activityIn).append(",")
+                .append("activityOut=").append(activityOut).append(",")
+                .append("description=").append(description).append(",")
+                .append("isTransient=").append(isTransient).append(",")
+                .append("secondaryLabel=").append(secondaryLabel)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback callback : mSignalCallbacks) {
                 callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
                         description, isTransient, secondaryLabel);
             }
         });
+
+
     }
 
     @Override
@@ -125,6 +149,25 @@
             final boolean activityOut, final CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, final CharSequence description,
             final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setMobileDataIndicators: ")
+                .append("statusIcon=").append(statusIcon).append(",")
+                .append("qsIcon=").append(qsIcon).append(",")
+                .append("statusType=").append(statusType).append(",")
+                .append("qsType=").append(qsType).append(",")
+                .append("activityIn=").append(activityIn).append(",")
+                .append("activityOut=").append(activityOut).append(",")
+                .append("typeContentDescription=").append(typeContentDescription).append(",")
+                .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+                .append(",")
+                .append("description=").append(description).append(",")
+                .append("isWide=").append(isWide).append(",")
+                .append("subId=").append(subId).append(",")
+                .append("roaming=").append(roaming).append(",")
+                .append("showTriangle=").append(showTriangle)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
@@ -138,6 +181,14 @@
     @Override
     public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
                 boolean noNetworksAvailable) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setConnectivityStatus: ")
+                .append("noDefaultNetwork=").append(noDefaultNetwork).append(",")
+                .append("noValidatedNetwork=").append(noValidatedNetwork).append(",")
+                .append("noNetworksAvailable=").append(noNetworksAvailable)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setConnectivityStatus(
@@ -148,6 +199,13 @@
 
     @Override
     public void setNoCallingStatus(boolean noCalling, int subId) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setNoCallingStatus: ")
+                .append("noCalling=").append(noCalling).append(",")
+                .append("subId=").append(subId)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setNoCallingStatus(noCalling, subId);
@@ -157,16 +215,35 @@
 
     @Override
     public void setSubs(List<SubscriptionInfo> subs) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setSubs: ")
+                .append("subs=").append(subs == null ? "" : subs.toString())
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget();
     }
 
     @Override
     public void setNoSims(boolean show, boolean simDetected) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setNoSims: ")
+                .append("show=").append(show).append(",")
+                .append("simDetected=").append(simDetected)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
     }
 
     @Override
     public void setMobileDataEnabled(boolean enabled) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setMobileDataEnabled: ")
+                .append("enabled=").append(enabled)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget();
     }
 
@@ -177,11 +254,23 @@
 
     @Override
     public void setEthernetIndicators(IconState icon) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setEthernetIndicators: ")
+                .append("icon=").append(icon)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
     }
 
     @Override
     public void setIsAirplaneMode(IconState icon) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setIsAirplaneMode: ")
+                .append("icon=").append(icon)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
     }
 
@@ -193,4 +282,25 @@
         obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
     }
 
+    protected void recordLastCallback(String callback) {
+        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+    }
+
+    /**
+     * Dump the Callback logs
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("  - CallbackHandler -----");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mHistory[i] != null) size++;
+        }
+        // Print out the previous states in ordered number.
+        for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+                i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mHistory[i & (HISTORY_SIZE - 1)]);
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 39472de..0fe338e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -46,6 +46,7 @@
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
 import com.android.settingslib.mobile.MobileStatusTracker;
+import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
 import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
@@ -54,6 +55,7 @@
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.BitSet;
 import java.util.List;
 import java.util.Map;
@@ -62,6 +64,8 @@
  * Monitors the mobile signal changes and update the SysUI icons.
  */
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
     private final TelephonyManager mPhone;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
@@ -90,6 +94,11 @@
     @VisibleForTesting
     MobileStatusTracker mMobileStatusTracker;
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mMobileStatusHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mMobileStatusHistoryIndex;
+
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(Context context, Config config, boolean hasMobileData,
@@ -126,12 +135,17 @@
         mCallback = new MobileStatusTracker.Callback() {
             @Override
             public void onMobileStatusChanged(boolean updateTelephony,
-                    MobileStatusTracker.MobileStatus mobileStatus) {
+                    MobileStatus mobileStatus) {
                 if (Log.isLoggable(mTag, Log.DEBUG)) {
                     Log.d(mTag, "onMobileStatusChanged="
                             + " updateTelephony=" + updateTelephony
                             + " mobileStatus=" + mobileStatus.toString());
                 }
+                String status = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append(mobileStatus.toString())
+                        .toString();
+                recordLastMobileStatus(status);
                 updateMobileStatus(mobileStatus);
                 if (updateTelephony) {
                     updateTelephony();
@@ -343,16 +357,8 @@
         return Utils.isInService(mServiceState);
     }
 
-    String getNonDefaultCarrierName() {
-        if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) {
-            return mCurrentState.networkNameData;
-        } else if (mSubscriptionInfo.getCarrierName() != null) {
-            return mSubscriptionInfo.getCarrierName().toString();
-        } else if (mSubscriptionInfo.getDisplayName() != null) {
-            return mSubscriptionInfo.getDisplayName().toString();
-        } else {
-            return "";
-        }
+    String getNetworkNameForCarrierWiFi() {
+        return mPhone.getSimOperatorName();
     }
 
     private boolean isRoaming() {
@@ -455,7 +461,7 @@
         return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
-    private void updateMobileStatus(MobileStatusTracker.MobileStatus mobileStatus) {
+    private void updateMobileStatus(MobileStatus mobileStatus) {
         mCurrentState.activityIn = mobileStatus.activityIn;
         mCurrentState.activityOut = mobileStatus.activityOut;
         mCurrentState.dataSim = mobileStatus.dataSim;
@@ -570,6 +576,10 @@
         notifyListenersIfNecessary();
     }
 
+    private void recordLastMobileStatus(String mobileStatus) {
+        mMobileStatusHistory[mMobileStatusHistoryIndex++ & (HISTORY_SIZE - 1)] = mobileStatus;
+    }
+
     @Override
     public void dump(PrintWriter pw) {
         super.dump(pw);
@@ -580,5 +590,17 @@
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
+        pw.println("  MobileStatusHistory");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mMobileStatusHistory[i] != null) size++;
+        }
+        // Print out the previous states in ordered number.
+        for (int i = mMobileStatusHistoryIndex + HISTORY_SIZE - 1;
+                i >= mMobileStatusHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous MobileStatus("
+                    + (mMobileStatusHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mMobileStatusHistory[i & (HISTORY_SIZE - 1)]);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e13e30b..80c7811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -76,6 +76,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
@@ -100,6 +101,8 @@
     private static final int EMERGENCY_VOICE_CONTROLLER = 200;
     private static final int EMERGENCY_NO_SUB = 300;
     private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
+    private static final int HISTORY_SIZE = 16;
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     private final Context mContext;
     private final TelephonyManager mPhone;
@@ -150,6 +153,11 @@
     // This list holds our ordering.
     private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mHistoryIndex;
+
     @VisibleForTesting
     boolean mListening;
 
@@ -307,6 +315,12 @@
             public void onLost(Network network) {
                 mLastNetwork = null;
                 mLastNetworkCapabilities = null;
+                String callback = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append("onLost: ")
+                        .append("network=").append(network)
+                        .toString();
+                recordLastNetworkCallback(callback);
                 updateConnectivity();
             }
 
@@ -327,6 +341,13 @@
                 }
                 mLastNetwork = network;
                 mLastNetworkCapabilities = networkCapabilities;
+                String callback = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append("onCapabilitiesChanged: ")
+                        .append("network=").append(network).append(",")
+                        .append("networkCapabilities=").append(networkCapabilities)
+                        .toString();
+                recordLastNetworkCallback(callback);
                 updateConnectivity();
             }
         };
@@ -528,9 +549,9 @@
         return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
     }
 
-    String getNonDefaultMobileDataNetworkName(int subId) {
+    String getNetworkNameForCarrierWiFi(int subId) {
         MobileSignalController controller = getControllerWithSubId(subId);
-        return controller != null ? controller.getNonDefaultCarrierName() : "";
+        return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
     private void notifyControllersMobileDataChanged() {
@@ -996,6 +1017,19 @@
         pw.print("  mEmergencySource=");
         pw.println(emergencyToString(mEmergencySource));
 
+        pw.println("  - DefaultNetworkCallback -----");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mHistory[i] != null) {
+                size++;
+            }
+        }
+        for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+                i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mHistory[i & (HISTORY_SIZE - 1)]);
+        }
+
         pw.println("  - config ------");
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -1006,6 +1040,8 @@
         mEthernetSignalController.dump(pw);
 
         mAccessPoints.dump(pw);
+
+        mCallbackHandler.dump(pw);
     }
 
     private static final String emergencyToString(int emergencySource) {
@@ -1235,6 +1271,10 @@
         return s;
     }
 
+    private void recordLastNetworkCallback(String callback) {
+        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+    }
+
     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 9380d91..8e833c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
@@ -80,6 +81,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LightBarController;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -99,6 +101,8 @@
 
     private final SendButtonTextWatcher mTextWatcher;
     private final TextView.OnEditorActionListener mEditorActionHandler;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>();
     private RemoteEditText mEditText;
     private ImageButton mSendButton;
     private ProgressBar mProgressBar;
@@ -121,12 +125,14 @@
     private boolean mResetting;
     private NotificationViewWrapper mWrapper;
     private Consumer<Boolean> mOnVisibilityChangedListener;
+    private NotificationRemoteInputManager.BouncerChecker mBouncerChecker;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mTextWatcher = new SendButtonTextWatcher();
         mEditorActionHandler = new EditorActionHandler();
         mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
         mStatusBarManagerService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
@@ -200,6 +206,11 @@
     }
 
     private void sendRemoteInput(Intent intent) {
+        if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
+            mEditText.hideIme();
+            return;
+        }
+
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
@@ -351,6 +362,11 @@
         }
     }
 
+    /** Populates the text field of the remote input with the given content. */
+    public void setEditTextContent(@Nullable CharSequence editTextContent) {
+        mEditText.setText(editTextContent);
+    }
+
     public void focusAnimated() {
         if (getVisibility() != VISIBLE) {
             Animator animator = ViewAnimationUtils.createCircularReveal(
@@ -552,6 +568,37 @@
         return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
     }
 
+    /**
+     * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker}
+     * that will be used to determine if the device needs to be unlocked before sending the
+     * RemoteInput.
+     */
+    public void setBouncerChecker(
+            @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) {
+        mBouncerChecker = bouncerChecker;
+    }
+
+    /** Registers a listener for focus-change events on the EditText */
+    public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+        mEditTextFocusChangeListeners.add(listener);
+    }
+
+    /** Removes a previously-added listener for focus-change events on the EditText */
+    public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+        mEditTextFocusChangeListeners.remove(listener);
+    }
+
+    /** Determines if the EditText has focus. */
+    public boolean editTextHasFocus() {
+        return mEditText != null && mEditText.hasFocus();
+    }
+
+    private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
+        for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
+            listener.onFocusChange(remoteEditText, focused);
+        }
+    }
+
     /** Handler for button click on send action in IME. */
     private class EditorActionHandler implements TextView.OnEditorActionListener {
 
@@ -603,6 +650,7 @@
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
+        private InputMethodManager mInputMethodManager;
         UserHandle mUser;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
@@ -621,6 +669,12 @@
             setOnReceiveContentListener(types, listener);
         }
 
+        private void hideIme() {
+            if (mInputMethodManager != null) {
+                mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+        }
+
         private void defocusIfNeeded(boolean animate) {
             if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
@@ -654,6 +708,9 @@
         @Override
         protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
             super.onFocusChanged(focused, direction, previouslyFocusedRect);
+            if (mRemoteInputView != null) {
+                mRemoteInputView.onEditTextFocusChanged(this, focused);
+            }
             if (!focused) {
                 defocusIfNeeded(true /* animate */);
             }
@@ -724,17 +781,16 @@
 
             if (mShowImeOnInputConnection && ic != null) {
                 Context targetContext = userContext != null ? userContext : getContext();
-                final InputMethodManager imm =
-                        targetContext.getSystemService(InputMethodManager.class);
-                if (imm != null) {
+                mInputMethodManager = targetContext.getSystemService(InputMethodManager.class);
+                if (mInputMethodManager != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
                     // work has completed.
                     post(new Runnable() {
                         @Override
                         public void run() {
-                            imm.viewClicked(RemoteEditText.this);
-                            imm.showSoftInput(RemoteEditText.this, 0);
+                            mInputMethodManager.viewClicked(RemoteEditText.this);
+                            mInputMethodManager.showSoftInput(RemoteEditText.this, 0);
                         }
                     });
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 4f4a504..738cab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -31,12 +31,11 @@
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
-import android.net.IConnectivityManager;
 import android.net.Network;
 import android.net.NetworkRequest;
+import android.net.VpnManager;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.security.KeyChain;
@@ -84,7 +83,7 @@
 
     private final Context mContext;
     private final ConnectivityManager mConnectivityManager;
-    private final IConnectivityManager mConnectivityManagerService;
+    private final VpnManager mVpnManager;
     private final DevicePolicyManager mDevicePolicyManager;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
@@ -116,8 +115,7 @@
                 context.getSystemService(Context.DEVICE_POLICY_SERVICE);
         mConnectivityManager = (ConnectivityManager)
                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mVpnManager = context.getSystemService(VpnManager.class);
         mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mBgExecutor = bgExecutor;
@@ -399,25 +397,19 @@
     private void updateState() {
         // Find all users with an active VPN
         SparseArray<VpnConfig> vpns = new SparseArray<>();
-        try {
-            for (UserInfo user : mUserManager.getUsers()) {
-                VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
-                if (cfg == null) {
+        for (UserInfo user : mUserManager.getUsers()) {
+            VpnConfig cfg = mVpnManager.getVpnConfig(user.id);
+            if (cfg == null) {
+                continue;
+            } else if (cfg.legacy) {
+                // Legacy VPNs should do nothing if the network is disconnected. Third-party
+                // VPN warnings need to continue as traffic can still go to the app.
+                LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id);
+                if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
                     continue;
-                } else if (cfg.legacy) {
-                    // Legacy VPNs should do nothing if the network is disconnected. Third-party
-                    // VPN warnings need to continue as traffic can still go to the app.
-                    LegacyVpnInfo legacyVpn = mConnectivityManagerService.getLegacyVpnInfo(user.id);
-                    if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
-                        continue;
-                    }
                 }
-                vpns.put(user.id, cfg);
             }
-        } catch (RemoteException rme) {
-            // Roll back to previous state
-            Log.e(TAG, "Unable to list active VPNs", rme);
-            return;
+            vpns.put(user.id, cfg);
         }
         mCurrentVpns = vpns;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 0552396..68d74ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -113,7 +113,9 @@
     private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
     private boolean mResumeUserOnGuestLogout = true;
     private boolean mSimpleUserSwitcher;
-    private boolean mAddUsersWhenLocked;
+    // When false, there won't be any visual affordance to add a new user from the keyguard even if
+    // the user is unlocked
+    private boolean mAddUsersFromLockScreen;
     private boolean mPauseRefreshUsers;
     private int mSecondaryUser = UserHandle.USER_NULL;
     private Intent mSecondaryUserServiceIntent;
@@ -204,7 +206,7 @@
         }
         mForcePictureLoadForUserId.clear();
 
-        final boolean addUsersWhenLocked = mAddUsersWhenLocked;
+        final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
         new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
             @SuppressWarnings("unchecked")
             @Override
@@ -554,7 +556,7 @@
     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
         public void onChange(boolean selfChange) {
             mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
-            mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
+            mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
             refreshUsers(UserHandle.USER_NULL);
         };
@@ -610,43 +612,26 @@
         }
 
         public int getUserCount() {
-            boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
-                    && mKeyguardStateController.isMethodSecure()
-                    && !mKeyguardStateController.canDismissLockScreen();
-            if (!secureKeyguardShowing) {
-                return getUsers().size();
-            }
-            // The lock screen is secure and showing. Filter out restricted records.
-            final int userSize = getUsers().size();
-            int count = 0;
-            for (int i = 0; i < userSize; i++) {
-                if (getUsers().get(i).isGuest) continue;
-                if (getUsers().get(i).isRestricted) {
-                    break;
-                } else {
-                    count++;
-                }
-            }
-            return count;
+            return countUsers(false);
         }
 
         @Override
         public int getCount() {
-            boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
-                    && mKeyguardStateController.isMethodSecure()
-                    && !mKeyguardStateController.canDismissLockScreen();
-            if (!secureKeyguardShowing) {
-                return getUsers().size();
-            }
-            // The lock screen is secure and showing. Filter out restricted records.
+            return countUsers(true);
+        }
+
+        private int countUsers(boolean includeGuest) {
+            boolean keyguardShowing = mKeyguardStateController.isShowing();
             final int userSize = getUsers().size();
             int count = 0;
             for (int i = 0; i < userSize; i++) {
-                if (getUsers().get(i).isRestricted) {
-                    break;
-                } else {
-                    count++;
+                if (getUsers().get(i).isGuest && !includeGuest) {
+                    continue;
                 }
+                if (getUsers().get(i).isRestricted && keyguardShowing) {
+                    break;
+                }
+                count++;
             }
             return count;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b2120d4..1fd2ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -147,7 +147,7 @@
         IconState qsIcon = new IconState(
                 mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
         CharSequence description =
-                mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId);
+                mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
                 dataContentDescriptionHtml, description, icons.isWide,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b7aa907..b42dde6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -75,6 +75,7 @@
 import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.RemoteTransitions;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -179,7 +180,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -204,7 +205,7 @@
     static Optional<ShellCommandHandler> provideShellCommandHandler(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
@@ -319,12 +320,21 @@
 
     @WMSingleton
     @Provides
-    static Optional<SplitScreen> provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
+    static Optional<SplitScreen> provideSplitScreen(
+            Optional<SplitScreenController> splitScreenController) {
+        return splitScreenController.map((controller) -> controller.asSplitScreen());
+    }
+
+    @WMSingleton
+    @Provides
+    static Optional<SplitScreenController> provideSplitScreenController(
+            ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            @ShellMainThread ShellExecutor mainExecutor) {
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
             return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
-                    rootTaskDisplayAreaOrganizer));
+                    rootTaskDisplayAreaOrganizer, mainExecutor));
         } else {
             return Optional.empty();
         }
@@ -388,6 +398,12 @@
 
     @WMSingleton
     @Provides
+    static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
+        return Transitions.asRemoteTransitions(transitions);
+    }
+
+    @WMSingleton
+    @Provides
     static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a2eaea1..70a7b7a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -34,6 +34,7 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -75,6 +76,8 @@
     NotificationIconAreaController mNotificationIconAreaController;
     @Mock
     ContentResolver mContentResolver;
+    @Mock
+    BroadcastDispatcher mBroadcastDispatcher;
 
     private KeyguardClockSwitchController mController;
 
@@ -94,7 +97,8 @@
                 mClockManager,
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
-                mContentResolver);
+                mContentResolver,
+                mBroadcastDispatcher);
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
index 53d84db..7b4f14d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
@@ -34,9 +34,9 @@
 
 import kotlin.math.ceil
 
-private val PAINT = arrayListOf(TextPaint().apply {
+private val PAINT = TextPaint().apply {
     textSize = 32f
-})
+}
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -49,10 +49,10 @@
 
     @Test
     fun testAnimationStarted() {
-        val layout = makeLayout("Hello, World", PAINT[0])
+        val layout = makeLayout("Hello, World", PAINT)
         val valueAnimator = mock(ValueAnimator::class.java)
         val textInterpolator = mock(TextInterpolator::class.java)
-        val paint = arrayListOf(mock(TextPaint::class.java))
+        val paint = mock(TextPaint::class.java)
         `when`(textInterpolator.targetPaint).thenReturn(paint)
 
         val textAnimator = TextAnimator(layout, {}).apply {
@@ -81,10 +81,10 @@
 
     @Test
     fun testAnimationNotStarted() {
-        val layout = makeLayout("Hello, World", PAINT[0])
+        val layout = makeLayout("Hello, World", PAINT)
         val valueAnimator = mock(ValueAnimator::class.java)
         val textInterpolator = mock(TextInterpolator::class.java)
-        val paint = arrayListOf(mock(TextPaint::class.java))
+        val paint = mock(TextPaint::class.java)
         `when`(textInterpolator.targetPaint).thenReturn(paint)
 
         val textAnimator = TextAnimator(layout, {}).apply {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
index 1206dab..149e179 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -21,9 +21,9 @@
 import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
-import android.text.TextPaint
 import android.text.TextDirectionHeuristic
 import android.text.TextDirectionHeuristics
+import android.text.TextPaint
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -40,13 +40,13 @@
     textSize = 32f
 }
 
-private val START_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val START_PAINT = TextPaint(PAINT).apply {
     fontVariationSettings = "'wght' 400"
-})
+}
 
-private val END_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val END_PAINT = TextPaint(PAINT).apply {
     fontVariationSettings = "'wght' 700"
-})
+}
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -67,16 +67,16 @@
         val layout = makeLayout(TEXT, PAINT)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         // Just after created TextInterpolator, it should have 0 progress.
         assertThat(interp.progress).isEqualTo(0f)
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
@@ -86,15 +86,15 @@
         val layout = makeLayout(TEXT, PAINT)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         interp.progress = 1f
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
@@ -104,10 +104,10 @@
         val layout = makeLayout(TEXT, PAINT)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         // We cannot expect exact text layout of the middle position since we don't use text shaping
@@ -115,9 +115,9 @@
         // end state.
         interp.progress = 0.5f
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0])
+        assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT)
             .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
-        assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0])
+        assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT)
             .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
     }
 
@@ -126,10 +126,10 @@
         val layout = makeLayout(TEXT, PAINT)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         interp.progress = 0.5f
@@ -148,16 +148,16 @@
         val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         // Just after created TextInterpolator, it should have 0 progress.
         assertThat(interp.progress).isEqualTo(0f)
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR)
                 .toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
@@ -168,16 +168,16 @@
         val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
 
         val interp = TextInterpolator(layout)
-        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.basePaint.set(START_PAINT)
         interp.onBasePaintModified()
 
-        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.targetPaint.set(END_PAINT)
         interp.onTargetPaintModified()
 
         // Just after created TextInterpolator, it should have 0 progress.
         assertThat(interp.progress).isEqualTo(0f)
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
                 .toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
new file mode 100644
index 0000000..b6729ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.systemui.keyguard;
+
+
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
+
+    private static final String TEST_MESSAGE = "test message";
+    private static final String TEST_MESSAGE_2 = "test message 2";
+
+    @Mock
+    private DelayableExecutor mExecutor;
+    @Mock
+    private KeyguardIndicationTextView mView;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Captor
+    private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+
+    private KeyguardIndicationRotateTextViewController mController;
+    private StatusBarStateController.StateListener mStatusBarStateListener;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE));
+        mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor,
+                mStatusBarStateController, LOCK_SCREEN_MODE_LAYOUT_1);
+        mController.onViewAttached();
+
+        verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+        mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+    }
+
+    @Test
+    public void testInitialState_noIndication() {
+        assertFalse(mController.hasIndications());
+    }
+
+    @Test
+    public void testShowOneIndication() {
+        // WHEN we add our first indication
+        final KeyguardIndication indication = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication, false);
+
+        // THEN
+        // - we see controller has an indication
+        // - the indication shows immediately since it's the only one
+        // - no next indication is scheduled since there's only one indication
+        assertTrue(mController.hasIndications());
+        verify(mView).switchIndication(indication);
+        verify(mExecutor, never()).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testShowTwoRotatingMessages() {
+        // GIVEN we already have an indication message
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+        reset(mView);
+
+        // WHEN we have a new indication type to display
+        final KeyguardIndication indication2 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication2, false);
+
+        // THEN
+        // - we don't immediately see the new message until the delay
+        // - next indication is scheduled
+        verify(mView, never()).switchIndication(indication2);
+        verify(mExecutor).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testUpdateCurrentMessage() {
+        // GIVEN we already have an indication message
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+        reset(mView);
+
+        // WHEN we have a new message for this indication type to display
+        final KeyguardIndication indication2 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication2, false);
+
+        // THEN
+        // - new indication is updated immediately
+        // - we don't schedule to show anything later
+        verify(mView).switchIndication(indication2);
+        verify(mExecutor, never()).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testUpdateRotatingMessageForUndisplayedIndication() {
+        // GIVEN we already have two indication messages
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+        reset(mView);
+        reset(mExecutor);
+
+        // WHEN we have a new message for an undisplayed indication type
+        final KeyguardIndication indication3 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication3, false);
+
+        // THEN
+        // - we don't immediately update
+        // - we don't schedule to show anything new
+        verify(mView, never()).switchIndication(indication3);
+        verify(mExecutor, never()).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testUpdateImmediately() {
+        // GIVEN we already have three indication messages
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+        mController.updateIndication(
+                INDICATION_TYPE_BATTERY, createIndication(), false);
+        reset(mView);
+        reset(mExecutor);
+
+        // WHEN we have a new message for a currently shown type that we want to show immediately
+        final KeyguardIndication indication4 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_BATTERY, indication4, true);
+
+        // THEN
+        // - we immediately update
+        // - we schedule a new delayable to show the next message later
+        verify(mView).switchIndication(indication4);
+        verify(mExecutor).executeDelayed(any(), anyLong());
+
+        // WHEN an already existing type is updated to show immediately
+        reset(mView);
+        reset(mExecutor);
+        final KeyguardIndication indication5 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication5, true);
+
+        // THEN
+        // - we immediately update
+        // - we schedule a new delayable to show the next message later
+        verify(mView).switchIndication(indication5);
+        verify(mExecutor).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testTransientIndication() {
+        // GIVEN we already have two indication messages
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+        reset(mView);
+        reset(mExecutor);
+
+        // WHEN we have a transient message
+        mController.showTransient(TEST_MESSAGE_2, false);
+
+        // THEN
+        // - we immediately update
+        // - we schedule a new delayable to show the next message later
+        verify(mView).switchIndication(any(KeyguardIndication.class));
+        verify(mExecutor).executeDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void testHideIndicationOneMessage() {
+        // GIVEN we have one indication message
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+
+        // WHEN we hide the current indication type
+        mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+
+        // THEN we immediately update the text to show no text
+        verify(mView).switchIndication(null);
+    }
+
+    @Test
+    public void testHideIndicationTwoMessages() {
+        // GIVEN we have two indication messages
+        final KeyguardIndication indication1 = createIndication();
+        final KeyguardIndication indication2 = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_OWNER_INFO, indication1, false);
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication2, false);
+        assertTrue(mController.isNextIndicationScheduled());
+
+        // WHEN we hide the current indication type
+        mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+
+        // THEN we show the next indication and there's no scheduled next indication
+        verify(mView).switchIndication(indication2);
+        assertFalse(mController.isNextIndicationScheduled());
+    }
+
+    @Test
+    public void testStartDozing() {
+        // WHEN the device is dozing
+        mStatusBarStateListener.onDozingChanged(true);
+
+        // THEN the view is GONE
+        verify(mView).setVisibility(View.GONE);
+    }
+
+    @Test
+    public void testStoppedDozing() {
+        // GIVEN we're dozing & we have an indication message
+        mStatusBarStateListener.onDozingChanged(true);
+        final KeyguardIndication indication = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication, false);
+        reset(mView);
+        reset(mExecutor);
+
+        // WHEN the device is no longer dozing
+        mStatusBarStateListener.onDozingChanged(false);
+
+        // THEN show the next message
+        verify(mView).switchIndication(indication);
+    }
+
+    @Test
+    public void testIsDozing() {
+        // GIVEN the device is dozing
+        mStatusBarStateListener.onDozingChanged(true);
+        reset(mView);
+
+        // WHEN an indication is updated
+        final KeyguardIndication indication = createIndication();
+        mController.updateIndication(
+                INDICATION_TYPE_DISCLOSURE, indication, false);
+
+        // THEN no message is shown since we're dozing
+        verify(mView, never()).switchIndication(any());
+    }
+
+    private KeyguardIndication createIndication() {
+        return new KeyguardIndication.Builder()
+                .setMessage(TEST_MESSAGE)
+                .setTextColor(ColorStateList.valueOf(Color.WHITE))
+                .build();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6..4ee2759 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.people;
 
 import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -52,6 +53,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
@@ -64,6 +66,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -82,10 +87,17 @@
 
     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
-    private static final String SHORTCUT_ID = "101";
+    private static final String SHORTCUT_ID_1 = "101";
+    private static final String SHORTCUT_ID_2 = "202";
+    private static final String SHORTCUT_ID_3 = "303";
+    private static final String SHORTCUT_ID_4 = "404";
     private static final String NOTIFICATION_KEY = "notification_key";
     private static final String NOTIFICATION_CONTENT = "notification_content";
     private static final String TEST_LOOKUP_KEY = "lookup_key";
+    private static final String NOTIFICATION_TEXT_1 = "notification_text_1";
+    private static final String NOTIFICATION_TEXT_2 = "notification_text_2";
+    private static final String NOTIFICATION_TEXT_3 = "notification_text_3";
+    private static final String NOTIFICATION_TEXT_4 = "notification_text_4";
     private static final int TEST_COLUMN_INDEX = 1;
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
@@ -97,20 +109,66 @@
             .build();
     private static final PeopleSpaceTile PERSON_TILE =
             new PeopleSpaceTile
-                    .Builder(SHORTCUT_ID, "username", ICON, new Intent())
+                    .Builder(SHORTCUT_ID_1, "username", ICON, new Intent())
                     .setNotificationKey(NOTIFICATION_KEY)
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
                     .build();
 
     private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
-            SHORTCUT_ID).setLongLabel(
+            SHORTCUT_ID_1).setLongLabel(
             "name").setPerson(PERSON)
             .build();
     private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
-            SHORTCUT_ID).setLongLabel(
+            SHORTCUT_ID_1).setLongLabel(
             "name")
             .build();
+    private final Notification mNotification1 = new Notification.Builder(mContext, "test")
+            .setContentTitle("TEST_TITLE")
+            .setContentText("TEST_TEXT")
+            .setShortcutId(SHORTCUT_ID_1)
+            .setStyle(new Notification.MessagingStyle(PERSON)
+                    .addMessage(new Notification.MessagingStyle.Message(
+                            NOTIFICATION_TEXT_1, 0, PERSON))
+                    .addMessage(new Notification.MessagingStyle.Message(
+                            NOTIFICATION_TEXT_2, 20, PERSON))
+                    .addMessage(new Notification.MessagingStyle.Message(
+                            NOTIFICATION_TEXT_3, 10, PERSON))
+            )
+            .build();
+    private final Notification mNotification2 = new Notification.Builder(mContext, "test2")
+            .setContentTitle("TEST_TITLE")
+            .setContentText("OTHER_TEXT")
+            .setShortcutId(SHORTCUT_ID_2)
+            .setStyle(new Notification.MessagingStyle(PERSON)
+                    .addMessage(new Notification.MessagingStyle.Message(
+                            NOTIFICATION_TEXT_4, 0, PERSON))
+            )
+            .build();
+    private final Notification mNotification3 = new Notification.Builder(mContext, "test2")
+            .setContentTitle("TEST_TITLE")
+            .setContentText("OTHER_TEXT")
+            .setShortcutId(SHORTCUT_ID_3)
+            .setStyle(new Notification.MessagingStyle(PERSON))
+            .build();
+    private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder()
+            .setNotification(mNotification1)
+            .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
+            .setUser(UserHandle.of(0))
+            .setPkg(PACKAGE_NAME)
+            .build();
+    private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder()
+            .setNotification(mNotification2)
+            .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build())
+            .setUser(UserHandle.of(0))
+            .setPkg(PACKAGE_NAME)
+            .build();
+    private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder()
+            .setNotification(mNotification3)
+            .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build())
+            .setUser(UserHandle.of(0))
+            .setPkg(PACKAGE_NAME)
+            .build();
 
     @Mock
     private NotificationListener mListenerService;
@@ -130,6 +188,8 @@
     private ContentResolver mMockContentResolver;
     @Mock
     private Context mMockContext;
+    @Mock
+    private NotificationEntryManager mNotificationEntryManager;
 
     @Before
     public void setUp() throws RemoteException {
@@ -152,15 +212,17 @@
                 isNull())).thenReturn(mMockCursor);
         when(mMockContext.getString(R.string.birthday_status)).thenReturn(
                 mContext.getString(R.string.birthday_status));
+        when(mNotificationEntryManager.getVisibleNotifications())
+                .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
     }
 
     @Test
     public void testGetTilesReturnsSortedListWithMultipleRecentConversations() throws Exception {
         // Ensure the less-recent Important conversation is before more recent conversations.
         ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID, false, 3);
+                SHORTCUT_ID_1, false, 3);
         ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID + 1,
+                SHORTCUT_ID_1 + 1,
                 true, 1);
         when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
                 new ParceledListSlice(Arrays.asList(
@@ -169,9 +231,9 @@
         // Ensure the non-Important conversation is sorted between these recent conversations.
         ConversationChannel recentConversationBeforeNonImportantConversation =
                 getConversationChannel(
-                        SHORTCUT_ID + 2, 4);
+                        SHORTCUT_ID_1 + 2, 4);
         ConversationChannel recentConversationAfterNonImportantConversation =
-                getConversationChannel(SHORTCUT_ID + 3,
+                getConversationChannel(SHORTCUT_ID_1 + 3,
                         2);
         when(mPeopleManager.getRecentConversations()).thenReturn(
                 new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -179,7 +241,8 @@
 
         List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
                 mContext, mNotificationManager, mPeopleManager,
-                mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+                mLauncherApps, mNotificationEntryManager)
+                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
 
         assertThat(orderedShortcutIds).containsExactly(
                 // Even though the oldest conversation, should be first since "important"
@@ -196,11 +259,11 @@
             throws Exception {
         // Ensure the less-recent Important conversation is before more recent conversations.
         ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID, false, 3);
+                SHORTCUT_ID_1, false, 3);
         ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID + 1, true, 3);
+                SHORTCUT_ID_1 + 1, true, 3);
         ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
-                SHORTCUT_ID + 2,
+                SHORTCUT_ID_1 + 2,
                 true, 1);
         when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
                 new ParceledListSlice(Arrays.asList(
@@ -210,9 +273,9 @@
         // Ensure the non-Important conversation is sorted between these recent conversations.
         ConversationChannel recentConversationBeforeNonImportantConversation =
                 getConversationChannel(
-                        SHORTCUT_ID + 3, 4);
+                        SHORTCUT_ID_1 + 3, 4);
         ConversationChannel recentConversationAfterNonImportantConversation =
-                getConversationChannel(SHORTCUT_ID + 4,
+                getConversationChannel(SHORTCUT_ID_1 + 4,
                         2);
         when(mPeopleManager.getRecentConversations()).thenReturn(
                 new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -220,7 +283,8 @@
 
         List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
                 mContext, mNotificationManager, mPeopleManager,
-                mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+                mLauncherApps, mNotificationEntryManager)
+                .stream().map(tile -> tile.getId()).collect(Collectors.toList());
 
         assertThat(orderedShortcutIds).containsExactly(
                 // Important conversations should be sorted at the beginning.
@@ -238,7 +302,7 @@
         Notification notification = new Notification.Builder(mContext, "test")
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
-                .setShortcutId(SHORTCUT_ID)
+                .setShortcutId(SHORTCUT_ID_1)
                 .build();
         StatusBarNotification sbn = new SbnBuilder()
                 .setNotification(notification)
@@ -341,52 +405,126 @@
 
     @Test
     public void testGetLastMessagingStyleMessage() {
-        Notification notification = new Notification.Builder(mContext, "test")
-                .setContentTitle("TEST_TITLE")
-                .setContentText("TEST_TEXT")
-                .setShortcutId(SHORTCUT_ID)
-                .setStyle(new Notification.MessagingStyle(PERSON)
-                        .addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON))
-                        .addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON))
-                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
-                )
-                .build();
         StatusBarNotification sbn = new SbnBuilder()
-                .setNotification(notification)
+                .setNotification(mNotification1)
                 .build();
 
         Notification.MessagingStyle.Message lastMessage =
                 PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
 
-        assertThat(lastMessage.getText()).isEqualTo("text2");
+        assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
     }
 
     @Test
-    public void testAugmentTileFromStorageWithNotification() {
+    public void testAugmentTileFromNotification() {
+        StatusBarNotification sbn = new SbnBuilder()
+                .setNotification(mNotification1)
+                .build();
+
         PeopleSpaceTile tile =
                 new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
+                        .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
+                .augmentTileFromNotification(tile, sbn);
 
-        assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
-        assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
+        assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
     }
 
     @Test
-    public void testAugmentTileFromStorageWithoutNotification() {
+    public void testAugmentTileFromNotificationNoContent() {
+        StatusBarNotification sbn = new SbnBuilder()
+                .setNotification(mNotification3)
+                .build();
+
         PeopleSpaceTile tile =
                 new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
+                        .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
+                .augmentTileFromNotification(tile, sbn);
 
         assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(null);
+        assertThat(actual.getNotificationContent()).isEqualTo(null);
+    }
+
+    @Test
+    public void testAugmentTileFromVisibleNotifications() {
+        PeopleSpaceTile tile =
+                new PeopleSpaceTile
+                        .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
+                        .build();
+        PeopleSpaceTile actual = PeopleSpaceUtils
+                .augmentTileFromVisibleNotifications(tile,
+                        Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+        assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+    }
+
+    @Test
+    public void testAugmentTileFromVisibleNotificationsDifferentShortcutId() {
+        PeopleSpaceTile tile =
+                new PeopleSpaceTile
+                        .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
+                        .build();
+        PeopleSpaceTile actual = PeopleSpaceUtils
+                .augmentTileFromVisibleNotifications(tile,
+                        Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+        assertThat(actual.getNotificationContent()).isEqualTo(null);
+    }
+
+    @Test
+    public void testAugmentTilesFromVisibleNotificationsSingleTile() {
+        PeopleSpaceTile tile =
+                new PeopleSpaceTile
+                        .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
+                        .build();
+        List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+                .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+
+        assertThat(actualList.size()).isEqualTo(1);
+        assertThat(actualList.get(0).getNotificationContent().toString())
+                .isEqualTo(NOTIFICATION_TEXT_2);
+
+        verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
+    }
+
+    @Test
+    public void testAugmentTilesFromVisibleNotificationsMultipleTiles() {
+        PeopleSpaceTile tile1 =
+                new PeopleSpaceTile
+                        .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(1)
+                        .build();
+        PeopleSpaceTile tile2 =
+                new PeopleSpaceTile
+                        .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
+                        .setPackageName(PACKAGE_NAME)
+                        .setUid(0)
+                        .build();
+        List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+                .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+                        mNotificationEntryManager);
+
+        assertThat(actualList.size()).isEqualTo(2);
+        assertThat(actualList.get(0).getNotificationContent().toString())
+                .isEqualTo(NOTIFICATION_TEXT_2);
+        assertThat(actualList.get(1).getNotificationContent().toString())
+                .isEqualTo(NOTIFICATION_TEXT_4);
+
+        verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
     }
 
     @Test
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 8c0afb8..9470141 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
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
 
 import androidx.preference.PreferenceManager;
 import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
     private static final long MIN_LINGER_DURATION = 5;
 
-    private static final String TEST_PACKAGE_A = "com.test.package_a";
+    private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
     private static final String TEST_PACKAGE_B = "com.test.package_b";
     private static final String TEST_CHANNEL_ID = "channel_id";
     private static final String TEST_CHANNEL_NAME = "channel_name";
     private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
     private static final String TEST_CONVERSATION_ID = "conversation_id";
     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+    private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
     private static final String SHORTCUT_ID = "101";
     private static final String OTHER_SHORTCUT_ID = "102";
-    private static final String NOTIFICATION_KEY = "notification_key";
-    private static final String NOTIFICATION_CONTENT = "notification_content";
+    private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+    private static final String NOTIFICATION_CONTENT = "message text";
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
     private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
     private static final PeopleSpaceTile PERSON_TILE =
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
-                    .setNotificationKey(NOTIFICATION_KEY)
+                    .setPackageName(TEST_PACKAGE_A)
+                    .setUid(0)
+                    .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
                     .build();
+    private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+            SHORTCUT_ID).setLongLabel("name").build();
 
     private PeopleSpaceWidgetManager mManager;
 
@@ -119,175 +125,101 @@
     @Mock
     private AppWidgetManager mAppWidgetManager;
     @Mock
-    private INotificationManager mINotificationManager;
+    private IPeopleManager mIPeopleManager;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+    @Captor
+    private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
 
     private final NoManSimulator mNoMan = new NoManSimulator();
     private final FakeSystemClock mClock = new FakeSystemClock();
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mManager =
                 new PeopleSpaceWidgetManager(mContext);
-        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
         mManager.attach(mListenerService);
 
         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
         NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
         mNoMan.addListener(serviceListener);
+        // Default to single People tile widgets.
         Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
 
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
-        editor.apply();
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
         Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
                 .thenReturn(options);
         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
-    public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+    public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+                .setContentTitle("TEST_TITLE")
+                .setContentText("TEST_TEXT")
+                .setStyle(new Notification.MessagingStyle(PERSON)
+                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                )
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(new SbnBuilder()
+                        .setNotification(notificationWithoutShortcut)
+                        .setPkg(TEST_PACKAGE_A)
+                        .build())
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
-        int[] widgetIdsArray = {1};
+    public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+        int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithoutPackageName)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -298,13 +230,12 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+    public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -316,45 +247,57 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+        verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, never())
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+    public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(4);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,222 @@
 
         verify(mAppWidgetManager, never())
                 .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(4);
+        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, times(1))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // 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);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+            throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // 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);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
     public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+            throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
-        Notification notification = new Notification.Builder(mContext)
+        Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(SHORTCUT_ID)
                 .build();
         StatusBarNotification sbn = new SbnBuilder()
-                .setNotification(notification)
+                .setNotification(notificationWithoutMessagingStyle)
+                .setPkg(TEST_PACKAGE_A)
+                .setUid(0)
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, never())
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
+        assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
+                .isEqualTo(PERSON_TILE);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, times(2))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(null);
+        assertThat(tile.getNotificationContent()).isEqualTo(null);
+        assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+                any());
     }
 
-    /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
-    private List<ConversationChannelWrapper> getConversationWithShortcutId() {
-        List<ConversationChannelWrapper> convos = new ArrayList<>();
-        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
-        convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
-                "name").build());
-        convos.add(convo1);
-        return convos;
+    /**
+     * Returns a single conversation associated with {@code shortcutId}.
+     */
+    private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build();
+        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+                0L, false);
+        return convo;
     }
 
-    private StatusBarNotification createConversationNotification(String shortcutId) {
-        Notification notification = new Notification.Builder(mContext)
+    private Notification createMessagingStyleNotification(String shortcutId) {
+        return new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(shortcutId)
                 .setStyle(new Notification.MessagingStyle(PERSON)
-                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                        .addMessage(
+                                new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                        PERSON))
                 )
                 .build();
+    }
+
+    private StatusBarNotification createConversationNotification(String shortcutId) {
+        Notification notification = createMessagingStyleNotification(shortcutId);
         return new SbnBuilder()
                 .setNotification(notification)
+                .setPkg(TEST_PACKAGE_A)
                 .build();
     }
+
+    private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+        widgetEditor.apply();
+
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(widgetId), shortcutId);
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index f86b465..072f7b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -100,12 +100,12 @@
 
     private val dialogProvider = object : PrivacyDialogController.DialogProvider {
         var list: List<PrivacyDialog.PrivacyElement>? = null
-        var starter: ((String) -> Unit)? = null
+        var starter: ((String, Int) -> Unit)? = null
 
         override fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String) -> Unit
+            starter: (String, Int) -> Unit
         ): PrivacyDialog {
             this.list = list
             this.starter = starter
@@ -210,7 +210,7 @@
     fun testSingleElementInList() {
         val usage = createMockPermGroupUsage(
                 packageName = TEST_PACKAGE_NAME,
-                uid = generateUidForUser(0),
+                uid = generateUidForUser(USER_ID),
                 permGroupName = PERM_CAMERA,
                 lastAccess = 5L,
                 isActive = true,
@@ -224,6 +224,8 @@
 
         val expected = PrivacyDialog.PrivacyElement(
                 type = PrivacyType.TYPE_CAMERA,
+                packageName = TEST_PACKAGE_NAME,
+                userId = USER_ID,
                 applicationName = TEST_PACKAGE_NAME,
                 attribution = TEST_ATTRIBUTION,
                 lastActiveTimestamp = 5L,
@@ -459,13 +461,28 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(PERM_MICROPHONE)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
         verify(activityStarter)
                 .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
 
-        assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_APPS)
-        assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME))
-                .isEqualTo(PERM_MICROPHONE)
+        assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+        assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PACKAGE_NAME))
+                .isEqualTo(TEST_PACKAGE_NAME)
+        assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle)
+                .isEqualTo(UserHandle.of(USER_ID))
+    }
+
+    @Test
+    fun testStartActivityCorrectIntent_enterpriseUser() {
+        controller.showDialog(context)
+        exhaustExecutors()
+
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID)
+        verify(activityStarter)
+                .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
+
+        assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle)
+                .isEqualTo(UserHandle.of(ENT_USER_ID))
     }
 
     @Test
@@ -473,7 +490,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(PERM_MICROPHONE)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP)
@@ -486,7 +503,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(PERM_MICROPHONE)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index eb5dd4e..28b44d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -40,8 +40,13 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class PrivacyDialogTest : SysuiTestCase() {
 
+    companion object {
+        private const val TEST_PACKAGE_NAME = "test_pkg"
+        private const val TEST_USER_ID = 0
+    }
+
     @Mock
-    private lateinit var starter: (String) -> Unit
+    private lateinit var starter: (String, Int) -> Unit
 
     private lateinit var dialog: PrivacyDialog
 
@@ -58,10 +63,12 @@
     }
 
     @Test
-    fun testStarterCalledWithCorrectPermGroupName() {
+    fun testStarterCalledWithCorrectParams() {
         val list = listOf(
                 PrivacyDialog.PrivacyElement(
                         PrivacyType.TYPE_MICROPHONE,
+                        TEST_PACKAGE_NAME,
+                        TEST_USER_ID,
                         "App",
                         null,
                         0L,
@@ -73,7 +80,7 @@
         dialog = PrivacyDialog(context, list, starter)
         dialog.show()
         dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
-        verify(starter).invoke(PrivacyType.TYPE_MICROPHONE.permGroupName)
+        verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
     }
 
     @Test
@@ -104,6 +111,8 @@
         val list = listOf(
                 PrivacyDialog.PrivacyElement(
                         PrivacyType.TYPE_CAMERA,
+                        TEST_PACKAGE_NAME,
+                        TEST_USER_ID,
                         "App",
                         null,
                         0L,
@@ -113,6 +122,8 @@
                 ),
                 PrivacyDialog.PrivacyElement(
                         PrivacyType.TYPE_MICROPHONE,
+                        TEST_PACKAGE_NAME,
+                        TEST_USER_ID,
                         "App",
                         null,
                         0L,
@@ -130,6 +141,8 @@
     fun testUsingText() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_CAMERA,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
                 "App",
                 null,
                 0L,
@@ -154,6 +167,8 @@
     fun testRecentText() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
                 "App",
                 null,
                 0L,
@@ -178,6 +193,8 @@
     fun testEnterprise() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
                 "App",
                 null,
                 0L,
@@ -198,6 +215,8 @@
     fun testPhoneCall() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
                 "App",
                 null,
                 0L,
@@ -218,6 +237,8 @@
     fun testAttribution() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
                 "App",
                 "attribution",
                 0L,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 78af20f..ffd747e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -75,6 +75,7 @@
         mFakeSettings = new FakeSettings();
 
         mTile = new ReduceBrightColorsTile(
+                true,
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
@@ -95,7 +96,6 @@
         assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
         assertEquals(mTile.getState().label.toString(),
                 mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-        assertEquals(mTile.getState().secondaryLabel.toString(), "");
     }
 
     @Test
@@ -128,13 +128,5 @@
         assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
         assertEquals(mTile.getState().label.toString(),
                 mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-
-        final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mUserTracker.getUserId());
-
-        assertEquals(
-                mContext.getString(
-                        R.string.quick_settings_reduce_bright_colors_secondary_label, intensity),
-                mTile.getState().secondaryLabel.toString());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index 451c78f..6d2b8e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -43,7 +43,7 @@
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -78,7 +78,7 @@
     @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
     @Mock private PackageManager mPackageManager;
     @Mock private SysUiState mMockSysUiState;
-    @Mock private Transitions mMockTransitions;
+    @Mock private RemoteTransitions mMockTransitions;
 
     @Before
     public void setUp() throws RemoteException {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 2c96535..0cae6742 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -18,6 +18,8 @@
 
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
 
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
@@ -27,13 +29,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Instrumentation;
@@ -51,7 +51,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserManager;
-import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.test.InstrumentationRegistry;
@@ -60,18 +59,21 @@
 
 import com.android.internal.app.IBatteryStats;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.KeyguardIndication;
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.After;
@@ -93,6 +95,7 @@
     private static final String ORGANIZATION_NAME = "organization";
 
     private String mDisclosureWithOrganization;
+    private String mDisclosureGeneric;
 
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
@@ -101,7 +104,7 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
-    private KeyguardIndicationTextView mDisclosure;
+    private KeyguardIndicationTextView mIndicationAreaBottom;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
     @Mock
@@ -118,8 +121,20 @@
     private IBatteryStats mIBatteryStats;
     @Mock
     private DockManager mDockManager;
+    @Mock
+    private KeyguardIndicationRotateTextViewController mRotateTextViewController;
     @Captor
     private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
+    @Captor
+    private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+    @Captor
+    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+    @Captor
+    private ArgumentCaptor<KeyguardIndication> mKeyguardIndicationCaptor;
+    private StatusBarStateController.StateListener mStatusBarStateListener;
+    private BroadcastReceiver mBroadcastReceiver;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
     private KeyguardIndicationTextView mTextView;
 
     private KeyguardIndicationController mController;
@@ -140,15 +155,15 @@
         mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
         mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
                 ORGANIZATION_NAME);
+        mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic);
 
         when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
         when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
         when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
 
-        when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
-                .thenReturn(mDisclosure);
+        when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom))
+                .thenReturn(mIndicationAreaBottom);
         when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
-        when(mDisclosure.getAlpha()).thenReturn(1f);
 
         mWakeLock = new WakeLockFake();
         mWakeLockBuilder = new WakeLockFake.Builder(mContext);
@@ -168,11 +183,15 @@
         mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
                 mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
                 mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
-                mUserManager);
+                mUserManager, mExecutor);
         mController.setIndicationArea(mIndicationArea);
+        verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+        mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+        verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any());
+        mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+        mController.mRotateTextViewController = mRotateTextViewController;
         mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         clearInvocations(mIBatteryStats);
-        verify(mDisclosure).getAlpha();
     }
 
     @Test
@@ -223,7 +242,7 @@
             createController();
             verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
             mController.setVisible(true);
-            mController.setDozing(true);
+            mStatusBarStateListener.onDozingChanged(true);
 
             mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
         });
@@ -241,7 +260,7 @@
             createController();
             verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
             mController.setVisible(true);
-            mController.setDozing(true);
+            mStatusBarStateListener.onDozingChanged(true);
 
             mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
         });
@@ -255,136 +274,108 @@
 
     @Test
     public void disclosure_unmanaged() {
+        createController();
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
         when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
-        createController();
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.GONE);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE);
+        verify(mRotateTextViewController, never()).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                any(), anyBoolean());
     }
 
     @Test
-    public void disclosure_deviceOwner_noOwnerName() {
+    public void disclosure_deviceOwner_noOrganizationName() {
+        createController();
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
         when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
-        createController();
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureGeneric);
     }
 
     @Test
-    public void disclosure_orgOwnedDeviceWithManagedProfile_noOwnerName() {
+    public void disclosure_orgOwnedDeviceWithManagedProfile_noOrganizationName() {
+        createController();
         when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
         when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
                 new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE)));
         when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null);
-        createController();
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureGeneric);
     }
 
     @Test
-    public void disclosure_hiddenWhenDozing() {
-        when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
-        when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+    public void disclosure_deviceOwner_withOrganizationName() {
         createController();
-
-        mController.setVisible(true);
-        mController.onDozeAmountChanged(1, 1);
-        mController.setDozing(true);
-
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).setAlpha(0f);
-        verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
-        verifyNoMoreInteractions(mDisclosure);
-    }
-
-    @Test
-    public void disclosure_visibleWhenDozing() {
-        when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
-        when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
-        createController();
-
-        mController.setVisible(true);
-        mController.onDozeAmountChanged(0, 0);
-        mController.setDozing(false);
-
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).setAlpha(1f);
-        verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
-        verifyNoMoreInteractions(mDisclosure);
-    }
-
-    @Test
-    public void disclosure_deviceOwner_withOwnerName() {
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
         when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
-        createController();
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureWithOrganization);
     }
 
     @Test
-    public void disclosure_orgOwnedDeviceWithManagedProfile_withOwnerName() {
+    public void disclosure_orgOwnedDeviceWithManagedProfile_withOrganizationName() {
+        createController();
         when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
         when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
                 new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE)));
         when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME);
-        createController();
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureWithOrganization);
     }
 
     @Test
     public void disclosure_updateOnTheFly() {
-        ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass(
-                BroadcastReceiver.class);
-        doNothing().when(mBroadcastDispatcher).registerReceiver(receiver.capture(), any());
-
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
         createController();
 
-        final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
-        reset(mDisclosure);
-
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
         when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
-        receiver.getValue().onReceive(mContext, new Intent());
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
-        verifyNoMoreInteractions(mDisclosure);
-        reset(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureGeneric);
+        reset(mRotateTextViewController);
 
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
         when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
-        receiver.getValue().onReceive(mContext, new Intent());
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.VISIBLE);
-        verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
-        verifyNoMoreInteractions(mDisclosure);
-        reset(mDisclosure);
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mDisclosureWithOrganization);
+        reset(mRotateTextViewController);
 
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
-        receiver.getValue().onReceive(mContext, new Intent());
+        sendUpdateDisclosureBroadcast();
 
-        verify(mDisclosure).setVisibility(View.GONE);
-        verifyNoMoreInteractions(mDisclosure);
+        verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE);
     }
 
     @Test
     public void transientIndication_holdsWakeLock_whenDozing() {
         createController();
 
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
         mController.showTransientIndication("Test");
 
         assertTrue(mWakeLock.isHeld());
@@ -394,7 +385,7 @@
     public void transientIndication_releasesWakeLock_afterHiding() {
         createController();
 
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
         mController.showTransientIndication("Test");
         mController.hideTransientIndication();
 
@@ -406,7 +397,7 @@
         mInstrumentation.runOnMainSync(() -> {
             createController();
 
-            mController.setDozing(true);
+            mStatusBarStateListener.onDozingChanged(true);
             mController.showTransientIndication("Test");
             mController.hideTransientIndicationDelayed(0);
         });
@@ -425,7 +416,7 @@
 
         mController.setVisible(true);
         mController.showTransientIndication("Test");
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
 
         assertThat(mTextView.getText()).isEqualTo("Test");
         assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
@@ -442,7 +433,7 @@
                 KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
                 BiometricSourceType.FACE);
         assertThat(mTextView.getText()).isEqualTo(message);
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
 
         assertThat(mTextView.getText()).isNotEqualTo(message);
     }
@@ -457,7 +448,7 @@
                 "A message", BiometricSourceType.FACE);
 
         assertThat(mTextView.getText()).isEqualTo(message);
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
 
         assertThat(mTextView.getText()).isNotEqualTo(message);
     }
@@ -528,8 +519,8 @@
     public void setDozing_noIBatteryCalls() throws RemoteException {
         createController();
         mController.setVisible(true);
-        mController.setDozing(true);
-        mController.setDozing(false);
+        mStatusBarStateListener.onDozingChanged(true);
+        mStatusBarStateListener.onDozingChanged(false);
         verify(mIBatteryStats, never()).computeChargeTimeRemaining();
     }
 
@@ -537,7 +528,6 @@
     public void updateMonitor_listener() {
         createController();
         verify(mKeyguardStateController).addCallback(eq(mController));
-        verify(mStatusBarStateController).addCallback(eq(mController));
         verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any());
     }
 
@@ -612,10 +602,14 @@
                 0 /* maxChargingWattage */, true /* present */);
 
         mController.getKeyguardCallback().onRefreshBatteryInfo(status);
-        mController.setDozing(true);
+        mStatusBarStateListener.onDozingChanged(true);
         mController.setVisible(true);
 
         String percentage = NumberFormat.getPercentInstance().format(90 / 100f);
         assertThat(mTextView.getText()).isEqualTo(percentage);
     }
+
+    private void sendUpdateDisclosureBroadcast() {
+        mBroadcastReceiver.onReceive(mContext, new Intent());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index fa253e6..b9fd75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -57,6 +58,7 @@
     @Mock private PowerManager mPowerManager;
     @Mock private TunerService mTunerService;
     @Mock private BatteryController mBatteryController;
+    @Mock private FeatureFlags mFeatureFlags;
 
     @Before
     public void setup() {
@@ -67,7 +69,8 @@
             mAlwaysOnDisplayPolicy,
             mPowerManager,
             mBatteryController,
-            mTunerService
+            mTunerService,
+            mFeatureFlags
         );
     }
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
index 0e77504..6068f0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -51,21 +51,21 @@
 
     @Test
     public void switchIndication_null_hideIndication() {
-        mKeyguardIndicationTextView.switchIndication(null /* text */);
+        mKeyguardIndicationTextView.switchIndication(null /* text */, null);
 
         assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
     }
 
     @Test
     public void switchIndication_emptyText_hideIndication() {
-        mKeyguardIndicationTextView.switchIndication("" /* text */);
+        mKeyguardIndicationTextView.switchIndication("" /* text */, null);
 
         assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
     }
 
     @Test
     public void switchIndication_newText_updateProperly() {
-        mKeyguardIndicationTextView.switchIndication("test_indication" /* text */);
+        mKeyguardIndicationTextView.switchIndication("test_indication" /* text */, null);
 
         assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index fa78ca6..c07ba72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -61,8 +61,10 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.controls.dagger.ControlsComponent;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
@@ -207,6 +209,10 @@
     @Mock
     private FeatureFlags mFeatureFlags;
     @Mock
+    private ControlsComponent mControlsComponent;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     @Mock
     private AmbientState mAmbientState;
@@ -300,7 +306,9 @@
                 mScrimController,
                 mMediaDataManager,
                 mAmbientState,
-                mFeatureFlags);
+                mFeatureFlags,
+                mControlsComponent,
+                mBroadcastDispatcher);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
                 mNotificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cae488a..253460d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -97,6 +97,7 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -258,6 +259,7 @@
     @Mock private DemoModeController mDemoModeController;
     @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
+    @Mock private FeatureFlags mFeatureFlags;
     private ShadeController mShadeController;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InitController mInitController = new InitController();
@@ -418,7 +420,8 @@
                 mNotificationShadeDepthControllerLazy,
                 mStatusBarTouchableRegionManager,
                 mNotificationIconAreaController,
-                mBrightnessSliderFactory);
+                mBrightnessSliderFactory,
+                mFeatureFlags);
 
         when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
                 mLockIconContainer);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
 
     private static final String TAG = "VpnDisconnected";
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mService;
     private int mUserId;
     private String mVpnPackage;
 
@@ -53,10 +50,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
         mUserId = UserHandle.myUserId();
-        mVpnPackage = getAlwaysOnVpnPackage();
+        final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+        mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
         if (mVpnPackage == null) {
             finish();
             return;
@@ -102,15 +98,6 @@
         }
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-            return mService.getAlwaysOnVpnPackage(mUserId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
-            return null;
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
 
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.net.VpnManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.Html;
@@ -48,7 +45,8 @@
 
     private String mPackage;
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mCm;  // TODO: switch entirely to VpnManager once VPN code moves
+    private VpnManager mVm;
 
     public ConfirmDialog() {
         this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mPackage = getCallingPackage();
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mCm = getSystemService(ConnectivityManager.class);
+        mVm = getSystemService(VpnManager.class);
 
-        if (prepareVpn()) {
+        if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
             setResult(RESULT_OK);
             finish();
             return;
@@ -74,7 +72,7 @@
             finish();
             return;
         }
-        final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+        final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
         // Can't prepare new vpn app when another vpn is always-on
         if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
             finish();
@@ -97,24 +95,6 @@
         button.setFilterTouchesWhenObscured(true);
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-           return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
-        } catch (RemoteException e) {
-            Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
-            // Fallback to null to show the dialog
-            return null;
-        }
-    }
-
-    private boolean prepareVpn() {
-        try {
-            return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         try {
-            if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+            if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
                 // Authorize this app to initiate VPN connections in the future without user
                 // intervention.
-                mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+                mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
                 setResult(RESULT_OK);
             }
         } catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
 
 package com.android.vpndialogs;
 
-import android.content.Context;
 import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
@@ -41,7 +39,7 @@
 
     private VpnConfig mConfig;
 
-    private IConnectivityManager mService;
+    private VpnManager mVm;
 
     private TextView mDuration;
     private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
         super.onCreate(savedInstanceState);
 
         try {
+            mVm = getSystemService(VpnManager.class);
 
-            mService = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
-            mConfig = mService.getVpnConfig(UserHandle.myUserId());
+            mConfig = mVm.getVpnConfig(UserHandle.myUserId());
 
             // mConfig can be null if we are a restricted user, in that case don't show this dialog
             if (mConfig == null) {
@@ -118,9 +114,9 @@
             } else if (which == DialogInterface.BUTTON_NEUTRAL) {
                 final int myUserId = UserHandle.myUserId();
                 if (mConfig.legacy) {
-                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
                 } else {
-                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
                 }
             }
         } catch (Exception e) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 8f093c7..065e2bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1113,7 +1113,7 @@
         try {
             final IBinder overlayWindowToken = new Binder();
             mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
-                    displayId);
+                    displayId, null /* options */);
             synchronized (mLock) {
                 mOverlayWindowTokens.put(displayId, overlayWindowToken);
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 2626654..a9e8ea0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -372,12 +372,11 @@
 
     @Override
     public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
-        final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId);
         synchronized (mLock) {
             if (mSecurityPolicy.canPerformGestures(this)) {
                 MotionEventInjector motionEventInjector =
                         mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
-                if (motionEventInjector != null && isTouchableDisplay) {
+                if (mWindowManagerService.isTouchOrFaketouchDevice()) {
                     motionEventInjector.injectEvents(
                             gestureSteps.getList(), mServiceInterface, sequence, displayId);
                 } else {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 25f4abe..903a071 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,12 +19,12 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -147,15 +147,15 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
         mMultiFingerGestures.add(
-                new MultiFingerMultiTapAndHold(
-                        mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
-        mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTapAndHold(
                         mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
+        mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this));
         // Three-finger taps.
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9f064c..eff410c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -55,7 +55,6 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
 import android.graphics.Point;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -119,7 +117,6 @@
 import com.android.internal.widget.IRemoteViewsFactory;
 import com.android.server.LocalServices;
 import com.android.server.WidgetBackupProvider;
-import com.android.server.policy.IconUtilities;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@
     private boolean mSafeMode;
     private int mMaxWidgetBitmapMemory;
 
-    private IconUtilities mIconUtilities;
-
     AppWidgetServiceImpl(Context context) {
         mContext = context;
     }
@@ -271,7 +266,6 @@
         mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
         mBackupRestoreController = new BackupRestoreController();
         mSecurityPolicy = new SecurityPolicy();
-        mIconUtilities = new IconUtilities(mContext);
 
         computeMaximumWidgetBitmapMemory();
         registerBroadcastReceiver();
@@ -578,44 +572,6 @@
         }
     }
 
-    private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // Load the unbadged application icon and pass it to the widget to appear on
-            // the masked view.
-            Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
-                    UserHandle.of(providerUserId));
-            PackageManager pm = userContext.getPackageManager();
-            Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
-            // Create a bitmap of the icon which is what the widget's remoteview requires.
-            icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
-            return mIconUtilities.createIconBitmap(icon);
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Fail to get application icon", e);
-            // Provider package removed, no need to mask its views as its state will be
-            // purged very soon.
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
-            PendingIntent onClickIntent) {
-        RemoteViews views = new RemoteViews(mContext.getPackageName(),
-                R.layout.work_widget_mask_view);
-        if (icon != null) {
-            views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
-        }
-        if (!showBadge) {
-            views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
-        }
-        if (onClickIntent != null) {
-            views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
-        }
-        return views;
-    }
-
     /**
      * Mask the target widget belonging to the specified provider, or all active widgets
      * of the provider if target widget == null.
@@ -625,59 +581,63 @@
         if (widgetCount == 0) {
             return;
         }
-        final String providerPackage = provider.id.componentName.getPackageName();
-        final int providerUserId = provider.getUserId();
-        Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
-        if (iconBitmap == null) {
-            return;
-        }
-        final boolean showBadge;
-        final Intent onClickIntent;
+        RemoteViews views = new RemoteViews(mContext.getPackageName(),
+                R.layout.work_widget_mask_view);
+        ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
+        final int appUserId = provider.getUserId();
+        boolean showBadge;
+
         final long identity = Binder.clearCallingIdentity();
         try {
+            final Intent onClickIntent;
+
             if (provider.maskedBySuspendedPackage) {
-                showBadge = mUserManager.hasBadge(providerUserId);
+                showBadge = mUserManager.hasBadge(appUserId);
                 final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
-                        providerPackage, providerUserId);
+                        appInfo.packageName, appUserId);
                 if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
                     onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
-                            providerUserId, true);
+                            appUserId, true);
                 } else {
                     final SuspendDialogInfo dialogInfo =
-                            mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
-                                    suspendingPackage, providerUserId);
+                            mPackageManagerInternal.getSuspendedDialogInfo(
+                                    appInfo.packageName, suspendingPackage, appUserId);
                     // onUnsuspend is null because we don't want to start any activity on
                     // unsuspending from a suspended widget.
                     onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
-                            providerPackage, suspendingPackage, dialogInfo, null, null,
-                            providerUserId);
+                            appInfo.packageName, suspendingPackage, dialogInfo, null, null,
+                            appUserId);
                 }
             } else if (provider.maskedByQuietProfile) {
                 showBadge = true;
-                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
-                        providerUserId);
+                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
             } else /* provider.maskedByLockedProfile */ {
                 showBadge = true;
-                onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
-                        providerUserId);
+                onClickIntent = mKeyguardManager
+                        .createConfirmDeviceCredentialIntent(null, null, appUserId);
                 if (onClickIntent != null) {
-                    onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
-                            | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    onClickIntent.setFlags(
+                            FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 }
             }
+
+            if (onClickIntent != null) {
+                views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
+                        PendingIntent.getActivity(mContext, 0, onClickIntent,
+                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+            }
+
+            Icon icon = appInfo.icon != 0
+                    ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
+                    : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+            views.setImageViewIcon(R.id.work_widget_app_icon, icon);
+            if (!showBadge) {
+                views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
+            }
+
             for (int j = 0; j < widgetCount; j++) {
                 Widget widget = provider.widgets.get(j);
                 if (targetWidget != null && targetWidget != widget) continue;
-                PendingIntent intent = null;
-                if (onClickIntent != null) {
-                    // Rare informational activity click is okay being
-                    // immutable; the tradeoff is more security in exchange for
-                    // losing bounds-based window animations
-                    intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
-                            onClickIntent,
-                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-                }
-                RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
                 if (widget.replaceWithMaskedViewsLocked(views)) {
                     scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
                 }
@@ -2687,6 +2647,8 @@
             info.icon = activityInfo.getIconResource();
             info.previewImage = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+            info.previewLayout = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, 0);
             info.autoAdvanceViewId = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
             info.resizeMode = sa.getInt(
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67f654e..6d72ca7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1492,7 +1492,7 @@
                 }
                 final Dataset dataset = (Dataset) result;
                 final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
-                if (!isPinnedDataset(oldDataset)) {
+                if (!isAuthResultDatasetEphemeral(oldDataset, data)) {
                     authenticatedResponse.getDatasets().set(datasetIdx, dataset);
                 }
                 autoFill(requestId, datasetIdx, dataset, false);
@@ -1513,6 +1513,21 @@
     }
 
     /**
+     * Returns whether the dataset returned from the authentication result is ephemeral or not.
+     * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
+     * information.
+     */
+    private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
+            @NonNull Bundle authResultData) {
+        if (authResultData.containsKey(
+                AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+            return authResultData.getBoolean(
+                    AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET);
+        }
+        return isPinnedDataset(oldDataset);
+    }
+
+    /**
      * A dataset can potentially have multiple fields, and it's possible that some of the fields'
      * has inline presentation and some don't. It's also possible that some of the fields'
      * inline presentation is pinned and some isn't. So the concept of whether a dataset is
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 55e3ef2..10b00d3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,9 +17,17 @@
 
 package com.android.server.companion;
 
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.content.Context.BIND_IMPORTANT;
+import static android.content.pm.PackageManager.MATCH_ALL;
+
+import static com.android.internal.util.CollectionUtils.any;
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static com.android.internal.util.CollectionUtils.filter;
 import static com.android.internal.util.CollectionUtils.find;
 import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.CollectionUtils.map;
 import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -40,22 +48,32 @@
 import android.app.role.RoleManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
 import android.companion.Association;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
+import android.companion.CompanionDeviceService;
 import android.companion.DeviceNotAssociatedException;
 import android.companion.ICompanionDeviceDiscoveryService;
 import android.companion.ICompanionDeviceManager;
+import android.companion.ICompanionDeviceService;
 import android.companion.IFindDeviceCallback;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.net.NetworkPolicyManager;
 import android.os.Binder;
@@ -77,6 +95,7 @@
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
@@ -115,6 +134,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -135,9 +155,14 @@
             CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
             ".DeviceDiscoveryService");
 
+    private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
+    private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
+
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "CompanionDeviceManagerService";
 
+    private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
+
     private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
     private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
 
@@ -146,18 +171,24 @@
     private static final String XML_ATTR_PACKAGE = "package";
     private static final String XML_ATTR_DEVICE = "device";
     private static final String XML_ATTR_PROFILE = "profile";
-    private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
+    private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
+    private static final String XML_ATTR_TIME_APPROVED = "time_approved";
     private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
 
     private final CompanionDeviceManagerImpl mImpl;
     private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
     private PowerWhitelistManager mPowerWhitelistManager;
     private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+    /** userId -> packageName -> serviceConnector */
+    private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
+            mDeviceListenerServiceConnectors;
     private IAppOpsService mAppOpsManager;
     private RoleManager mRoleManager;
     private BluetoothAdapter mBluetoothAdapter;
+    private UserManager mUserManager;
 
     private IFindDeviceCallback mFindDeviceCallback;
+    private ScanCallback mBleScanCallback = new BleScanCallback();
     private AssociationRequest mRequest;
     private String mCallingPackage;
     private AndroidFuture<Association> mOngoingDeviceDiscovery;
@@ -165,9 +196,16 @@
 
     private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
             new BluetoothDeviceConnectedListener();
+    private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
     private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+    private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
+    private UnbindDeviceListenersRunnable
+            mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
+    private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
+            new ArrayMap<>();
 
     private final Object mLock = new Object();
+    private final Handler mMainHandler = Handler.getMain();
 
     /** userId -> [association] */
     @GuardedBy("mLock")
@@ -189,6 +227,7 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
+        mUserManager = context.getSystemService(UserManager.class);
 
         Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
         mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -201,6 +240,16 @@
             }
         };
 
+        mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
+                ServiceConnector<ICompanionDeviceService>>>() {
+            @NonNull
+            @Override
+            protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
+                    int userId) {
+                return new ArrayMap<>();
+            }
+        };
+
         registerPackageMonitor();
     }
 
@@ -208,10 +257,13 @@
         new PackageMonitor() {
             @Override
             public void onPackageRemoved(String packageName, int uid) {
+                int userId = getChangingUserId();
                 updateAssociations(
                         as -> CollectionUtils.filter(as,
                                 a -> !Objects.equals(a.getPackageName(), packageName)),
-                        getChangingUserId());
+                        userId);
+
+                unbindDevicePresenceListener(packageName, userId);
             }
 
             @Override
@@ -225,6 +277,15 @@
         }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
     }
 
+    private void unbindDevicePresenceListener(String packageName, int userId) {
+        ServiceConnector<ICompanionDeviceService> deviceListener =
+                mDeviceListenerServiceConnectors.forUser(userId)
+                        .remove(packageName);
+        if (deviceListener != null) {
+            deviceListener.unbind();
+        }
+    }
+
     @Override
     public void onStart() {
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -233,11 +294,17 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            // Init Bluetooth
             mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
             if (mBluetoothAdapter != null) {
                 mBluetoothAdapter.registerBluetoothConnectionCallback(
                         getContext().getMainExecutor(),
                         mBluetoothDeviceConnectedListener);
+                getContext().registerReceiver(
+                        mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
+                initBleScanning();
+            } else {
+                Log.w(LOG_TAG, "No BluetoothAdapter available");
             }
         }
     }
@@ -287,7 +354,7 @@
 
     @Override
     public void binderDied() {
-        Handler.getMain().post(this::cleanup);
+        mMainHandler.post(this::cleanup);
     }
 
     private void cleanup() {
@@ -399,7 +466,7 @@
                 checkCallerIsSystemOr(callingPackage, userId);
                 checkUsesFeature(callingPackage, getCallingUserId());
             }
-            return new ArrayList<>(CollectionUtils.map(
+            return new ArrayList<>(map(
                     getAllAssociations(userId, callingPackage),
                     a -> a.getDeviceMacAddress()));
         }
@@ -497,7 +564,7 @@
                 return true;
             }
 
-            return CollectionUtils.any(
+            return any(
                     getAllAssociations(userId, packageName),
                     a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
         }
@@ -506,22 +573,18 @@
         public void registerDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, true);
         }
 
         @Override
         public void unregisterDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, false);
         }
 
-        private void checkCanRegisterObserverService(String packageName, String deviceAddress)
-                throws RemoteException {
+        private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
+                boolean active) throws RemoteException {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
                     "[un]registerDevicePresenceListenerService");
@@ -537,6 +600,21 @@
                         + " is not associated with device " + deviceAddress
                         + " for user " + userId));
             }
+
+            updateAssociations(associations -> map(associations, association -> {
+                if (Objects.equals(association.getPackageName(), packageName)
+                        && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    return new Association(
+                            association.getUserId(),
+                            association.getDeviceMacAddress(),
+                            association.getPackageName(),
+                            association.getDeviceProfile(),
+                            active, /* notifyOnDeviceNearby */
+                            association.getTimeApprovedMs());
+                } else {
+                    return association;
+                }
+            }));
         }
 
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -565,6 +643,15 @@
         }
 
         @Override
+        public boolean canPairWithoutPrompt(
+                String packageName, String deviceMacAddress, int userId) {
+            return CollectionUtils.any(
+                    getAllAssociations(userId, packageName, deviceMacAddress),
+                    a -> System.currentTimeMillis() - a.getTimeApprovedMs()
+                            < PAIR_WITHOUT_PROMPT_WINDOW_MS);
+        }
+
+        @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
@@ -693,6 +780,10 @@
         if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
             grantDeviceProfile(association);
         }
+
+        if (association.isNotifyOnDeviceNearby()) {
+            restartBleScan();
+        }
     }
 
     private void exemptFromAutoRevoke(String packageName, int uid) {
@@ -795,10 +886,12 @@
                                         association.getDeviceMacAddress());
                         if (association.getDeviceProfile() != null) {
                             tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
-                            tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
+                            tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
                                     Boolean.toString(
-                                            association.isKeepProfilePrivilegesWhenDeviceAway()));
+                                            association.isNotifyOnDeviceNearby()));
                         }
+                        tag.attribute(null, XML_ATTR_TIME_APPROVED,
+                                Long.toString(association.getTimeApprovedMs()));
                         tag.endTag(null, XML_TAG_ASSOCIATION);
                     });
 
@@ -835,16 +928,41 @@
     }
 
     private List<UserInfo> getAllUsers() {
-        return getContext().getSystemService(UserManager.class).getUsers();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return mUserManager.getUsers();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
-    @Nullable
     private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
         return CollectionUtils.filter(
                 getAllAssociations(userId),
                 a -> Objects.equals(packageFilter, a.getPackageName()));
     }
 
+    private Set<Association> getAllAssociations() {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            ArraySet<Association> result = new ArraySet<>();
+            for (UserInfo user : mUserManager.getAliveUsers()) {
+                result.addAll(getAllAssociations(user.id));
+            }
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private Set<Association> getAllAssociations(
+            int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
+        return CollectionUtils.filter(
+                getAllAssociations(userId),
+                a -> Objects.equals(packageFilter, a.getPackageName())
+                        && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+    }
+
     private Set<Association> readAllAssociations(int userId) {
         final AtomicFile file = getStorageFileForUser(userId);
 
@@ -865,13 +983,15 @@
 
                     final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
                     final boolean persistentGrants = Boolean.valueOf(
-                            parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
+                            parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
+                    final long timeApproved = parseLongOrDefault(
+                            parser.getAttributeValue(null, XML_ATTR_TIME_APPROVED), 0L);
 
                     if (appPackage == null || deviceAddress == null) continue;
 
                     result = ArrayUtils.add(result,
                             new Association(userId, deviceAddress, appPackage,
-                                    profile, persistentGrants));
+                                    profile, persistentGrants, timeApproved));
                 }
                 return result;
             } catch (XmlPullParserException | IOException e) {
@@ -896,6 +1016,8 @@
                 }
             }
         }
+
+        onDeviceNearby(address);
     }
 
     private void grantDeviceProfile(Association association) {
@@ -919,6 +1041,267 @@
 
     void onDeviceDisconnected(String address) {
         mCurrentlyConnectedDevices.remove(address);
+
+        onDeviceDisappeared(address);
+    }
+
+    private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
+            Association a) {
+        return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
+                a.getPackageName(),
+                pkg -> createDeviceListenerServiceConnector(a));
+    }
+
+    private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
+            Association a) {
+        List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
+                new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
+        List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
+                info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
+        if (packageResolveInfos.size() != 1) {
+            Log.w(LOG_TAG, "Device presence listener package must have exactly one "
+                    + "CompanionDeviceService, but " + a.getPackageName()
+                    + " has " + packageResolveInfos.size());
+            return new ServiceConnector.NoOp<>();
+        }
+        ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
+        Log.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
+        return new ServiceConnector.Impl<>(getContext(),
+                new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
+                BIND_IMPORTANT,
+                a.getUserId(),
+                ICompanionDeviceService.Stub::asInterface);
+    }
+
+    private class BleScanCallback extends ScanCallback {
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "onScanResult(callbackType = "
+                        + callbackType + ", result = " + result + ")");
+            }
+
+            onDeviceNearby(result.getDevice().getAddress());
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            for (int i = 0, size = results.size(); i < size; i++) {
+                onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
+                // ignore - this might happen if BT tries to auto-restore scans for us in the
+                // future
+                Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
+            } else {
+                Log.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
+            }
+        }
+    }
+
+    private class BleStateBroadcastReceiver extends BroadcastReceiver {
+
+        final IntentFilter mIntentFilter =
+                new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+            int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+            Log.i(LOG_TAG, "Received BT state transition broadcast: "
+                    + BluetoothAdapter.nameForState(previousState)
+                    + " -> " + BluetoothAdapter.nameForState(newState));
+
+            boolean bleOn = newState == BluetoothAdapter.STATE_ON
+                    || newState == BluetoothAdapter.STATE_BLE_ON;
+            if (bleOn) {
+                if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
+                    startBleScan();
+                } else {
+                    Log.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null");
+                }
+            }
+        }
+    }
+
+    private class UnbindDeviceListenersRunnable implements Runnable {
+
+        public String getJobId(String address) {
+            return "CDM_deviceGone_unbind_" + address;
+        }
+
+        @Override
+        public void run() {
+            int size = mDevicesLastNearby.size();
+            for (int i = 0; i < size; i++) {
+                String address = mDevicesLastNearby.keyAt(i);
+                Date lastNearby = mDevicesLastNearby.valueAt(i);
+
+                if (System.currentTimeMillis() - lastNearby.getTime()
+                        >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+                    for (Association association : getAllAssociations(address)) {
+                        if (association.isNotifyOnDeviceNearby()) {
+                            getDeviceListenerServiceConnector(association).unbind();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private class TriggerDeviceDisappearedRunnable implements Runnable {
+
+        private final String mAddress;
+
+        TriggerDeviceDisappearedRunnable(String address) {
+            mAddress = address;
+        }
+
+        public void schedule() {
+            mMainHandler.removeCallbacks(this);
+            mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
+        }
+
+        @Override
+        public void run() {
+            onDeviceDisappeared(mAddress);
+        }
+    }
+
+    private Set<Association> getAllAssociations(String deviceAddress) {
+        List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+        Set<Association> result = new ArraySet<>();
+        for (int i = 0, size = aliveUsers.size(); i < size; i++) {
+            UserInfo user = aliveUsers.get(i);
+            for (Association association : getAllAssociations(user.id)) {
+                if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    result.add(association);
+                }
+            }
+        }
+        return result;
+    }
+
+    private void onDeviceNearby(String address) {
+        Date timestamp = new Date();
+        Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
+
+        cancelUnbindDeviceListener(address);
+
+        mTriggerDeviceDisappearedRunnables
+                .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
+                .schedule();
+
+        // Avoid spamming the app if device is already known to be nearby
+        boolean justAppeared = oldTimestamp == null
+                || timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
+        if (justAppeared) {
+            for (Association association : getAllAssociations(address)) {
+                if (association.isNotifyOnDeviceNearby()) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Device " + address
+                                + " managed by " + association.getPackageName()
+                                + " is nearby on " + timestamp);
+                    }
+                    getDeviceListenerServiceConnector(association).run(
+                            service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+                }
+            }
+        }
+    }
+
+    private void onDeviceDisappeared(String address) {
+        boolean hasDeviceListeners = false;
+        for (Association association : getAllAssociations(address)) {
+            if (association.isNotifyOnDeviceNearby()) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Device " + address
+                            + " managed by " + association.getPackageName()
+                            + " disappeared; last seen on " + mDevicesLastNearby.get(address));
+                }
+
+                getDeviceListenerServiceConnector(association).run(
+                        service -> service.onDeviceDisappeared(address));
+                hasDeviceListeners = true;
+            }
+        }
+
+        cancelUnbindDeviceListener(address);
+        if (hasDeviceListeners) {
+            mMainHandler.postDelayed(
+                    mUnbindDeviceListenersRunnable,
+                    mUnbindDeviceListenersRunnable.getJobId(address),
+                    DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
+        }
+    }
+
+    private void cancelUnbindDeviceListener(String address) {
+        mMainHandler.removeCallbacks(
+                mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
+    }
+
+    private void initBleScanning() {
+        Log.i(LOG_TAG, "initBleScanning()");
+
+        boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
+                new BluetoothAdapter.ServiceLifecycleCallback() {
+                    @Override
+                    public void onBluetoothServiceUp() {
+                        Log.i(LOG_TAG, "Bluetooth stack is up");
+                        startBleScan();
+                    }
+
+                    @Override
+                    public void onBluetoothServiceDown() {
+                        Log.w(LOG_TAG, "Bluetooth stack is down");
+                    }
+                });
+        if (bluetoothReady) {
+            startBleScan();
+        }
+    }
+
+    void startBleScan() {
+        Log.i(LOG_TAG, "startBleScan()");
+
+        List<ScanFilter> filters = getBleScanFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+        BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (scanner == null) {
+            Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
+        } else {
+            scanner.startScan(
+                    filters,
+                    new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+                    mBleScanCallback);
+        }
+    }
+
+    void restartBleScan() {
+        mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+        startBleScan();
+    }
+
+    private List<ScanFilter> getBleScanFilters() {
+        ArrayList<ScanFilter> result = new ArrayList<>();
+        ArraySet<String> addressesSeen = new ArraySet<>();
+        for (Association association : getAllAssociations()) {
+            String address = association.getDeviceMacAddress();
+            if (addressesSeen.contains(address)) {
+                continue;
+            }
+            if (association.isNotifyOnDeviceNearby()) {
+                result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
+                addressesSeen.add(address);
+            }
+        }
+        return result;
     }
 
     private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
@@ -934,6 +1317,15 @@
         return result;
     }
 
+    private static long parseLongOrDefault(String str, long def) {
+        try {
+            return Long.parseLong(str);
+        } catch (NumberFormatException e) {
+            Log.w(LOG_TAG, "Failed to parse", e);
+            return def;
+        }
+    }
+
     private class ShellCmd extends ShellCommand {
         public static final String USAGE = "help\n"
                 + "list USER_ID\n"
@@ -962,7 +1354,8 @@
                         int userId = getNextArgInt();
                         String pkg = getNextArgRequired();
                         String address = getNextArgRequired();
-                        addAssociation(new Association(userId, address, pkg, null, false));
+                        addAssociation(new Association(userId, address, pkg, null, false,
+                                System.currentTimeMillis()));
                     }
                     break;
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e01c4df..83a5036 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,11 +100,10 @@
     libs: [
         "services.net",
         "android.hardware.light-V2.0-java",
-        "android.hardware.gnss-java",
-        "android.hardware.power-java",
+        "android.hardware.gnss-V1-java",
+        "android.hardware.power-V1-java",
         "android.hardware.power-V1.0-java",
-        "android.hardware.vibrator-java",
-        "android.net.ipsec.ike.stubs.module_lib",
+        "android.hardware.vibrator-V2-java",
         "app-compat-annotations",
         "framework-tethering.stubs.module_lib",
         "service-permission.stubs.system_server",
@@ -128,22 +127,22 @@
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
         "android.hardware.health-V2.1-java",
-        "android.hardware.light-java",
+        "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.1-java",
-        "android.hardware.biometrics.face-java",
+        "android.hardware.biometrics.face-V1-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
-        "android.hardware.biometrics.fingerprint-java",
+        "android.hardware.biometrics.fingerprint-V1-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
-        "android.hardware.rebootescrow-java",
+        "android.hardware.rebootescrow-V1-java",
         "android.hardware.soundtrigger-V2.3-java",
-        "android.hardware.power.stats-java",
+        "android.hardware.power.stats-V1-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
-        "dnsresolver_aidl_interface-java",
+        "dnsresolver_aidl_interface-V7-java",
         "icu4j_calendar_astronomer",
         "netd-client",
         "overlayable_policy_aidl-java",
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..737a9e4 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager.ComponentInfoFlags;
 import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.os.Bundle;
 import android.os.Handler;
@@ -49,8 +50,8 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -535,17 +536,17 @@
      * Set which overlay to use for a package.
      * @param userId The user for which to update the overlays.
      * @param targetPackageName The package name of the package for which to update the overlays.
-     * @param overlayPackageNames The complete list of overlay packages that should be enabled for
-     *                            the target. Previously enabled overlays not specified in the list
-     *                            will be disabled. Pass in null or an empty list to disable
-     *                            all overlays. The order of the items is significant if several
-     *                            overlays modify the same resource.
+     * @param overlayPaths  The complete list of overlay paths that should be enabled for
+     *                      the target. Previously enabled overlays not specified in the list
+     *                      will be disabled. Pass in null or empty paths to disable all overlays.
+     *                      The order of the items is significant if several overlays modify the
+     *                      same resource.
      * @param outUpdatedPackageNames An output list that contains the package names of packages
      *                               affected by the update of enabled overlays.
      * @return true if all packages names were known by the package manager, false otherwise
      */
     public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
-            List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
+            @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames);
 
     /**
      * Resolves an activity intent, allowing instant apps to be resolved.
@@ -997,28 +998,6 @@
     public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
 
     /**
-     * Register to listen for loading progress of an installed package.
-     * @param packageName The name of the installed package
-     * @param callback To loading reporting progress
-     * @param userId The user under which to check.
-     * @return Whether the registration was successful. It can fail if the package has not been
-     *          installed yet.
-     */
-    public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
-            @NonNull InstalledLoadingProgressCallback callback, int userId);
-
-    /**
-     * Unregister to stop listening to loading progress of an installed package
-     * @param packageName The name of the installed package
-     * @param callback To unregister
-     * @return True if the callback is removed from registered callback list. False is the callback
-     *         does not exist on the registered callback list, which can happen if the callback has
-     *         already been unregistered.
-     */
-    public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
-            @NonNull InstalledLoadingProgressCallback callback);
-
-    /**
      * Returns the string representation of a known package. For example,
      * {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
      *
@@ -1137,7 +1116,8 @@
      */
     public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional, @Checksum.Type int required,
-            @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
             @NonNull Executor executor, @NonNull Handler handler);
 
     /**
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+    private BundleUtils() {
+    }
+
+    /**
+     * Returns true if {@code in} is null or empty.
+     */
+    public static boolean isEmpty(@Nullable Bundle in) {
+        return (in == null) || (in.size() == 0);
+    }
+
+    /**
+     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
+     * bundle.
+     *
+     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+     * {@link Bundle#EMPTY})
+     */
+    public static @NonNull Bundle clone(@Nullable Bundle in) {
+        return (in != null) ? new Bundle(in) : new Bundle();
+    }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bf9d564..42b6a7f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -45,6 +45,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -120,6 +121,7 @@
 import android.net.NetworkTestResultParcelable;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.QosCallbackException;
@@ -130,12 +132,14 @@
 import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.TetheringManager;
+import android.net.TransportInfo;
 import android.net.UidRange;
 import android.net.UidRangeParcel;
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.VpnService;
+import android.net.VpnTransportInfo;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
@@ -281,15 +285,18 @@
     // connect anyway?" dialog after the user selects a network that doesn't validate.
     private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
 
-    // Default to 30s linger time-out. Modifiable only for testing.
+    // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+    private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
 
     // The maximum number of network request allowed per uid before an exception is thrown.
     private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
 
     @VisibleForTesting
     protected int mLingerDelayMs;  // Can't be final, or test subclass constructors can't change it.
+    @VisibleForTesting
+    protected int mNascentDelayMs;
 
     // How long to delay to removal of a pending intent based request.
     // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -1034,7 +1041,8 @@
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
                 -1, NetworkRequest.Type.REQUEST);
-        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder(),
+                null /* attributionTag */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
         mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
@@ -1063,6 +1071,8 @@
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+        // TODO: Consider making the timer customizable.
+        mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
 
         mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
         mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
@@ -1240,6 +1250,7 @@
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1249,6 +1260,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > -1) {
             netCap.addTransportType(transportType);
@@ -1302,7 +1314,7 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    null, networkRequest, new Binder()));
+                    null, networkRequest, new Binder(), null /* attributionTag */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
                     /* callOnUnavailable */ false);
@@ -1637,7 +1649,7 @@
 
     @Override
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-                int userId, String callingPackageName) {
+                int userId, String callingPackageName, @Nullable String callingAttributionTag) {
         // The basic principle is: if an app's traffic could possibly go over a
         // network, without the app doing anything multinetwork-specific,
         // (hence, by "default"), then include that network's capabilities in
@@ -1668,7 +1680,8 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName));
+                                nc, mDeps.getCallingUid(), callingPackageName,
+                                callingAttributionTag));
             }
         }
 
@@ -1681,7 +1694,8 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName));
+                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    callingAttributionTag));
                 }
             }
         }
@@ -1756,12 +1770,13 @@
     }
 
     @Override
-    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName,
+            @Nullable String callingAttributionTag) {
         mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
-                mDeps.getCallingUid(), callingPackageName);
+                mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
     @VisibleForTesting
@@ -1780,11 +1795,12 @@
         return newNc;
     }
 
-    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         final long token = Binder.clearCallingIdentity();
         try {
             return mLocationPermissionChecker.checkLocationPermission(
-                    callerPkgName, null /* featureId */, callerUid, null /* message */);
+                    callerPkgName, callingAttributionTag, callerUid, null /* message */);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1793,7 +1809,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1802,7 +1819,8 @@
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
         if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
         } else {
             newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
@@ -1819,7 +1837,8 @@
         }
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
         }
         // Reset owner uid if the app has no location permission.
         if (!hasLocationPermission) {
@@ -1877,7 +1896,8 @@
         final ArrayList<NetworkState> result = new ArrayList<>();
         for (Network network : getAllNetworks()) {
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-            if (nai != null) {
+            // TODO: Consider include SUSPENDED networks.
+            if (nai != null && nai.networkInfo.isConnected()) {
                 // TODO (b/73321673) : NetworkState contains a copy of the
                 // NetworkCapabilities, which may contain UIDs of apps to which the
                 // network applies. Should the UIDs be cleared so as not to leak or
@@ -2030,7 +2050,7 @@
                 mHandler.sendMessage(mHandler.obtainMessage(
                         EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
                         new PrivateDnsValidationUpdate(netId,
-                                InetAddress.parseNumericAddress(ipAddress),
+                                InetAddresses.parseNumericAddress(ipAddress),
                                 hostname, validated)));
             } catch (IllegalArgumentException e) {
                 loge("Error parsing ip address in validation event");
@@ -3334,7 +3354,7 @@
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
         //    one lingered request, set inactive.
         nai.updateInactivityTimer();
-        if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
+        if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
             if (DBG) log("Unsetting inactive " + nai.toShortString());
             nai.unsetInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
@@ -3588,10 +3608,7 @@
         // As this request was not satisfied on rematch and thus never had any scores sent to the
         // factories, send null now for each request of type REQUEST.
         for (final NetworkRequest req : nri.mRequests) {
-            if (!req.isRequest()) {
-                continue;
-            }
-            sendUpdatedScoreToFactories(req, null);
+            if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
         }
     }
 
@@ -3631,7 +3648,7 @@
                 return true;
         }
 
-        if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+        if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
             return false;
         }
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -3768,7 +3785,7 @@
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
 
         if (null != nri.getActiveRequest()) {
-            if (nri.getActiveRequest().isRequest()) {
+            if (!nri.getActiveRequest().isListen()) {
                 removeSatisfiedNetworkRequestFromNetwork(nri);
             } else {
                 nri.setSatisfier(null, null);
@@ -5516,7 +5533,7 @@
         }
 
         // The network currently satisfying this NRI. Only one request in an NRI can have a
-        // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier.
+        // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier.
         @Nullable
         private NetworkAgentInfo mSatisfier;
         NetworkAgentInfo getSatisfier() {
@@ -5538,6 +5555,8 @@
         final int mPid;
         final int mUid;
         final Messenger messenger;
+        @Nullable
+        final String mCallingAttributionTag;
 
         /**
          * Get the list of UIDs this nri applies to.
@@ -5551,7 +5570,8 @@
             return uids;
         }
 
-        NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
+        NetworkRequestInfo(NetworkRequest r, PendingIntent pi,
+                @Nullable String callingAttributionTag) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
             mPendingIntent = pi;
@@ -5560,9 +5580,11 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
         }
 
-        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
+        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder,
+                @Nullable String callingAttributionTag) {
             super();
             messenger = m;
             mRequests = initializeRequests(r);
@@ -5572,6 +5594,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -5581,7 +5604,7 @@
         }
 
         NetworkRequestInfo(NetworkRequest r) {
-            this(r, null);
+            this(r, null /* pi */, null /* callingAttributionTag */);
         }
 
         // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -5726,6 +5749,7 @@
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
+        final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities;
         final int callingUid = mDeps.getCallingUid();
         final NetworkRequest.Type reqType;
         try {
@@ -5736,11 +5760,15 @@
         switch (reqType) {
             case TRACK_DEFAULT:
                 // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities}
-                // is unused and will be replaced by the one from the default network request.
-                // This allows callers to keep track of the system default network.
+                // is unused and will be replaced by ones appropriate for the caller.
+                // This allows callers to keep track of the default network for their app.
                 networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
                 enforceAccessPermission();
                 break;
+            case TRACK_SYSTEM_DEFAULT:
+                enforceSettingsPermission();
+                networkCapabilities = new NetworkCapabilities(defaultNc);
+                break;
             case BACKGROUND_REQUEST:
                 enforceNetworkStackOrSettingsPermission();
                 // Fall-through since other checks are the same with normal requests.
@@ -5759,6 +5787,7 @@
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
                 Binder.getCallingPid(), callingUid, callingPackageName);
+
         // Set the UID range for this request to the single UID of the requester, or to an empty
         // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
         // This will overwrite any allowed UIDs in the requested capabilities. Though there
@@ -5774,9 +5803,20 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
+        // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
+        // copied from the default request above. (This is necessary to ensure, for example, that
+        // the callback does not leak sensitive information to unprivileged apps.) Check that the
+        // changes don't alter request matching.
+        if (reqType == NetworkRequest.Type.TRACK_SYSTEM_DEFAULT &&
+                (!networkCapabilities.equalRequestableCapabilities(defaultNc))) {
+            Log.wtf(TAG, "TRACK_SYSTEM_DEFAULT capabilities don't match default request: "
+                    + networkCapabilities + " vs. " + defaultNc);
+        }
+
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
         if (timeoutMs > 0) {
             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
@@ -5863,7 +5903,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (DBG) log("pendingRequest for " + nri);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
                 nri));
@@ -5907,7 +5948,8 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5927,7 +5969,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5936,7 +5979,8 @@
 
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation, @NonNull String callingPackageName) {
+            PendingIntent operation, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
@@ -5950,7 +5994,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (VDBG) log("pendingListenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -7004,8 +7049,8 @@
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest nr = nai.requestAt(i);
-            // Don't send listening requests to factories. b/17393458
-            if (nr.isListen()) continue;
+            // Don't send listening or track default request to factories. b/17393458
+            if (!nr.isRequest()) continue;
             sendUpdatedScoreToFactories(nr, nai);
         }
     }
@@ -7067,10 +7112,10 @@
         ensureRunningOnConnectivityServiceThread();
         for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
             for (final NetworkRequest req : nri.mRequests) {
-                if (req.isListen() && nri.getActiveRequest() == req) {
+                if (!req.isRequest() && nri.getActiveRequest() == req) {
                     break;
                 }
-                if (req.isListen()) {
+                if (!req.isRequest()) {
                     continue;
                 }
                 // Only set the nai for the request it is satisfying.
@@ -7165,7 +7210,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
@@ -7184,7 +7230,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7220,8 +7267,8 @@
         if (nai.numRequestNetworkRequests() != 0) {
             for (int i = 0; i < nai.numNetworkRequests(); i++) {
                 NetworkRequest nr = nai.requestAt(i);
-                // Ignore listening requests.
-                if (nr.isListen()) continue;
+                // Ignore listening and track default requests.
+                if (!nr.isRequest()) continue;
                 loge("Dead network still had at least " + nr);
                 break;
             }
@@ -7244,7 +7291,7 @@
             // Tear the network down.
             teardownUnneededNetwork(oldNetwork);
         } else {
-            // Put the network in the background.
+            // Put the network in the background if it doesn't satisfy any foreground request.
             updateCapabilitiesForNetwork(oldNetwork);
         }
     }
@@ -7498,6 +7545,14 @@
             } else {
                 if (VDBG || DDBG) log("   accepting network in place of null");
             }
+
+            // To prevent constantly CPU wake up for nascent timer, if a network comes up
+            // and immediately satisfies a request then remove the timer. This will happen for
+            // all networks except in the case of an underlying network for a VCN.
+            if (newSatisfier.isNascent()) {
+                newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
+            }
+
             newSatisfier.unlingerRequest(newRequest.requestId);
             if (!newSatisfier.addRequest(newRequest)) {
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
@@ -7640,19 +7695,19 @@
             }
         }
 
-        // Update the linger state before processing listen callbacks, because the background
-        // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+        // Update the inactivity state before processing listen callbacks, because the background
+        // computation depends on whether the network is inactive. Don't send the LOSING callbacks
         // just yet though, because they have to be sent after the listens are processed to keep
         // backward compatibility.
-        final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+        final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
-            // Rematching may have altered the linger state of some networks, so update all linger
-            // timers. updateLingerState reads the state from the network agent and does nothing
-            // if the state has not changed : the source of truth is controlled with
-            // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
-            // called while rematching the individual networks above.
+            // Rematching may have altered the inactivity state of some networks, so update all
+            // inactivity timers. updateInactivityState reads the state from the network agent
+            // and does nothing if the state has not changed : the source of truth is controlled
+            // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+            // have been called while rematching the individual networks above.
             if (updateInactivityState(nai, now)) {
-                lingeredNetworks.add(nai);
+                inactiveNetworks.add(nai);
             }
         }
 
@@ -7669,7 +7724,11 @@
             processNewlySatisfiedListenRequests(nai);
         }
 
-        for (final NetworkAgentInfo nai : lingeredNetworks) {
+        for (final NetworkAgentInfo nai : inactiveNetworks) {
+            // For nascent networks, if connecting with no foreground request, skip broadcasting
+            // LOSING for backward compatibility. This is typical when mobile data connected while
+            // wifi connected with mobile data always-on enabled.
+            if (nai.isNascent()) continue;
             notifyNetworkLosing(nai, now);
         }
 
@@ -7910,6 +7969,15 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
+            // Before first rematching networks, put an inactivity timer without any request, this
+            // allows {@code updateInactivityState} to update the state accordingly and prevent
+            // tearing down for any {@code unneeded} evaluation in this period.
+            // Note that the timer will not be rescheduled since the expiry time is
+            // fixed after connection regardless of the network satisfying other requests or not.
+            // But it will be removed as soon as the network satisfies a request for the first time.
+            networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+                    SystemClock.elapsedRealtime(), mNascentDelayMs);
+
             // Consider network even though it is not yet validated.
             rematchAllNetworksAndRequests();
 
@@ -8412,22 +8480,11 @@
         }
     }
 
-    /**
-     * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
-     * for testing.
-     */
-    private Vpn enforceActiveVpnOrNetworkStackPermission() {
-        if (checkNetworkStackPermission()) {
-            return null;
-        }
-        synchronized (mVpns) {
-            Vpn vpn = getVpnIfOwner();
-            if (vpn != null) {
-                return vpn;
-            }
-        }
-        throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
-                + "permission");
+    private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
+        if (vpn == null) return VpnManager.TYPE_VPN_NONE;
+        final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
+        if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE;
+        return ((VpnTransportInfo) ti).type;
     }
 
     /**
@@ -8437,14 +8494,6 @@
      * connection is not found.
      */
     public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
-        final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
-
-        // Only VpnService based VPNs should be able to get this information.
-        if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) {
-            throw new SecurityException(
-                    "getConnectionOwnerUid() not allowed for non-VpnService VPNs");
-        }
-
         if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
             throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
         }
@@ -8452,8 +8501,15 @@
         final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
                 connectionInfo.local, connectionInfo.remote);
 
-        /* Filter out Uids not associated with the VPN. */
-        if (vpn != null && !vpn.appliesToUid(uid)) {
+        if (uid == INVALID_UID) return uid;  // Not found.
+
+        // Connection owner UIDs are visible only to the network stack and to the VpnService-based
+        // VPN, if any, that applies to the UID that owns the connection.
+        if (checkNetworkStackPermission()) return uid;
+
+        final NetworkAgentInfo vpn = getVpnForUid(uid);
+        if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE
+                || vpn.networkCapabilities.getOwnerUid() != Binder.getCallingUid()) {
             return INVALID_UID;
         }
 
@@ -9100,6 +9156,7 @@
             }
         }
     }
+
     /**
      * Registers {@link QosSocketFilter} with {@link IQosCallback}.
      *
@@ -9149,4 +9206,10 @@
     public void unregisterQosCallback(@NonNull final IQosCallback callback) {
         mQosCallbackTracker.unregisterCallback(callback);
     }
+
+    @Override
+    public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+        // TODO http://b/176495594 track multiple default networks with networkPreferences
+        if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+    }
 }
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2..a83c981 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -16,12 +16,6 @@
 
 package com.android.server;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -33,10 +27,15 @@
 import android.os.SystemProperties;
 import android.util.Slog;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
 /**
  * A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
- * into the Linux RNG.
+ * for the Linux kernel RNG.
  *
  * <p>When a Linux system starts up, the entropy pool associated with
  * {@code /dev/random} may be in a fairly predictable state.  Applications which
@@ -45,15 +44,8 @@
  * this effect, it's helpful to carry the entropy pool information across
  * shutdowns and startups.
  *
- * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
- * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
- * EntropyMixer periodically runs to save a block of output from Linux RNG on
- * disk. This mixing is done in a way that does not increase the Linux RNG's
- * entropy estimate is not increased. This is to avoid having to trust/verify
- * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
- *
  * <p>This class was modeled after the script in the
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
  * random(4) manual page</a>.
  */
 public class EntropyMixer extends Binder {
@@ -64,7 +56,6 @@
     private static final long START_NANOTIME = System.nanoTime();
 
     private final String randomDevice;
-    private final String hwRandomDevice;
     private final String entropyFile;
 
     /**
@@ -80,7 +71,6 @@
                 Slog.e(TAG, "Will not process invalid message");
                 return;
             }
-            addHwRandomEntropy();
             writeEntropy();
             scheduleEntropyWriter();
         }
@@ -94,25 +84,21 @@
     };
 
     public EntropyMixer(Context context) {
-        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
+        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
     }
 
     /** Test only interface, not for public use */
     public EntropyMixer(
             Context context,
             String entropyFile,
-            String randomDevice,
-            String hwRandomDevice) {
+            String randomDevice) {
         if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
-        if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
         if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
 
         this.randomDevice = randomDevice;
-        this.hwRandomDevice = hwRandomDevice;
         this.entropyFile = entropyFile;
         loadInitialEntropy();
         addDeviceSpecificEntropy();
-        addHwRandomEntropy();
         writeEntropy();
         scheduleEntropyWriter();
         IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -192,23 +178,6 @@
         }
     }
 
-    /**
-     * Mixes in the output from HW RNG (if present) into the Linux RNG.
-     */
-    private void addHwRandomEntropy() {
-        if (!new File(hwRandomDevice).exists()) {
-            // HW RNG not present/exposed -- ignore
-            return;
-        }
-
-        try {
-            RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
-            Slog.i(TAG, "Added HW RNG output to entropy pool");
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
-        }
-    }
-
     private static String getSystemDir() {
         File dataDir = Environment.getDataDirectory();
         File systemDir = new File(dataDir, "system");
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7d65156..1ad0176 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -890,7 +890,7 @@
             ZramWriteback.scheduleZramWriteback(mContext);
         }
 
-        updateTranscodeEnabled();
+        configureTranscoding();
     }
 
     /**
@@ -922,7 +922,7 @@
         }
     }
 
-    private void updateTranscodeEnabled() {
+    private void configureTranscoding() {
         // See MediaProvider TranscodeHelper#getBooleanProperty for more information
         boolean transcodeEnabled = false;
         boolean defaultValue = true;
@@ -935,6 +935,15 @@
                     "transcode_enabled", defaultValue);
         }
         SystemProperties.set("sys.fuse.transcode_enabled", String.valueOf(transcodeEnabled));
+
+        if (transcodeEnabled) {
+            LocalServices.getService(ActivityManagerInternal.class)
+                    .registerAnrController((packageName, uid) -> {
+                        // TODO: Retrieve delay from ExternalStorageService that can check
+                        // transcoding status
+                        return SystemProperties.getInt("sys.fuse.transcode_anr_delay_ms", 0);
+                    });
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e96fd39..96f832d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -50,6 +50,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetworkStackConstants;
 
 import java.io.UncheckedIOException;
 import java.net.Inet4Address;
@@ -280,10 +281,12 @@
 
         // Add global routes (but as non-default, non-internet providing network)
         if (allowIPv4) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
         }
         if (allowIPv6) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
         }
 
         final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 916bec2..27210da 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -291,8 +292,9 @@
                 @NonNull VcnContext vcnContext,
                 @NonNull ParcelUuid subscriptionGroup,
                 @NonNull VcnConfig config,
-                @NonNull TelephonySubscriptionSnapshot snapshot) {
-            return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
+                @NonNull TelephonySubscriptionSnapshot snapshot,
+                @NonNull VcnSafemodeCallback safemodeCallback) {
+            return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
         }
 
         /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -438,7 +440,12 @@
         // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
         //                    VCN.
 
-        final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
+        final VcnSafemodeCallbackImpl safemodeCallback =
+                new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+        final Vcn newInstance =
+                mDeps.newVcn(
+                        mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
         mVcns.put(subscriptionGroup, newInstance);
 
         // Now that a new VCN has started, notify all registered listeners to refresh their
@@ -536,7 +543,7 @@
         }
     }
 
-    /** Get current configuration list for testing purposes */
+    /** Get current VCNs for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Map<ParcelUuid, Vcn> getAllVcns() {
         synchronized (mLock) {
@@ -634,21 +641,61 @@
         }
 
         boolean isVcnManagedNetwork = false;
+        boolean isRestrictedCarrierWifi = false;
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             synchronized (mLock) {
                 ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
 
-                // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
-                if (mVcns.containsKey(subGroup)) {
-                    isVcnManagedNetwork = true;
+                Vcn vcn = mVcns.get(subGroup);
+                if (vcn != null) {
+                    if (vcn.isActive()) {
+                        isVcnManagedNetwork = true;
+                    }
+
+                    if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+                        // Carrier WiFi always restricted if VCN exists (even in safe mode).
+                        isRestrictedCarrierWifi = true;
+                    }
                 }
             }
         }
+
         if (isVcnManagedNetwork) {
             networkCapabilities.removeCapability(
                     NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
         }
 
+        if (isRestrictedCarrierWifi) {
+            networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        }
+
         return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
     }
+
+    /** Callback for signalling when a Vcn has entered Safemode. */
+    public interface VcnSafemodeCallback {
+        /** Called by a Vcn to signal that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+    private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+        @NonNull private final ParcelUuid mSubGroup;
+
+        private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+            mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+        }
+
+        @Override
+        public void onEnteredSafemode() {
+            synchronized (mLock) {
+                // Ignore if this subscription group doesn't exist anymore
+                if (!mVcns.containsKey(mSubGroup)) {
+                    return;
+                }
+
+                notifyAllPolicyListenersLocked();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 6a816af..e7e5d67 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -111,6 +111,7 @@
     private final AppOpsManager mAppOps;
     private final NativeWrapper mNativeWrapper;
     private final VibratorManagerRecords mVibratorManagerRecords;
+    private final long mCapabilities;
     private final int[] mVibratorIds;
     private final SparseArray<VibratorController> mVibrators;
     private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@@ -127,18 +128,28 @@
     private VibrationScaler mVibrationScaler;
     private InputDeviceDelegate mInputDeviceDelegate;
 
-    static native long nativeInit();
+    static native long nativeInit(OnSyncedVibrationCompleteListener listener);
 
     static native long nativeGetFinalizer();
 
+    static native long nativeGetCapabilities(long nativeServicePtr);
+
     static native int[] nativeGetVibratorIds(long nativeServicePtr);
 
+    static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds);
+
+    static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId);
+
+    static native void nativeCancelSynced(long nativeServicePtr);
+
     @VisibleForTesting
     VibratorManagerService(Context context, Injector injector) {
         mContext = context;
         mHandler = injector.createHandler(Looper.myLooper());
+
+        VibrationCompleteListener listener = new VibrationCompleteListener(this);
         mNativeWrapper = injector.getNativeWrapper();
-        mNativeWrapper.init();
+        mNativeWrapper.init(listener);
 
         int dumpLimit = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
@@ -153,6 +164,7 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
+        mCapabilities = mNativeWrapper.getCapabilities();
         int[] vibratorIds = mNativeWrapper.getVibratorIds();
         if (vibratorIds == null) {
             mVibratorIds = new int[0];
@@ -161,11 +173,17 @@
             // Keep original vibrator id order, which might be meaningful.
             mVibratorIds = vibratorIds;
             mVibrators = new SparseArray<>(mVibratorIds.length);
-            VibrationCompleteListener listener = new VibrationCompleteListener(this);
             for (int vibratorId : vibratorIds) {
                 mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
             }
         }
+
+        // Reset the hardware to a default state, in case this is a runtime restart instead of a
+        // fresh boot.
+        mNativeWrapper.cancelSynced();
+        for (int i = 0; i < mVibrators.size(); i++) {
+            mVibrators.valueAt(i).off();
+        }
     }
 
     /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
@@ -502,6 +520,17 @@
         }
     }
 
+    private void onSyncedVibrationComplete(long vibrationId) {
+        synchronized (mLock) {
+            if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
+                }
+                mCurrentVibration.syncedVibrationComplete();
+            }
+        }
+    }
+
     private void onVibrationComplete(int vibratorId, long vibrationId) {
         synchronized (mLock) {
             if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
@@ -839,13 +868,22 @@
     private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
 
         @Override
-        public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
-            // TODO(b/167946816): call IVibratorManager to prepare
+        public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
+            if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
+                // This sync step requires capabilities this device doesn't have, skipping sync...
+                return false;
+            }
+            return mNativeWrapper.prepareSynced(vibratorIds);
         }
 
         @Override
-        public void triggerSyncedVibration(long vibrationId) {
-            // TODO(b/167946816): call IVibratorManager to trigger
+        public boolean triggerSyncedVibration(long vibrationId) {
+            return mNativeWrapper.triggerSynced(vibrationId);
+        }
+
+        @Override
+        public void cancelSyncedVibration() {
+            mNativeWrapper.cancelSynced();
         }
 
         @Override
@@ -868,12 +906,19 @@
         }
     }
 
+    /** Listener for synced vibration completion callbacks from native. */
+    @VisibleForTesting
+    public interface OnSyncedVibrationCompleteListener {
+
+        /** Callback triggered when synced vibration is complete. */
+        void onComplete(long vibrationId);
+    }
+
     /**
-     * Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak
-     * reference to this service.
+     * Implementation of listeners to native vibrators with a weak reference to this service.
      */
     private static final class VibrationCompleteListener implements
-            VibratorController.OnVibrationCompleteListener {
+            VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener {
         private WeakReference<VibratorManagerService> mServiceRef;
 
         VibrationCompleteListener(VibratorManagerService service) {
@@ -881,6 +926,14 @@
         }
 
         @Override
+        public void onComplete(long vibrationId) {
+            VibratorManagerService service = mServiceRef.get();
+            if (service != null) {
+                service.onSyncedVibrationComplete(vibrationId);
+            }
+        }
+
+        @Override
         public void onComplete(int vibratorId, long vibrationId) {
             VibratorManagerService service = mServiceRef.get();
             if (service != null) {
@@ -952,9 +1005,9 @@
         private long mNativeServicePtr = 0;
 
         /** Returns native pointer to newly created controller and connects with HAL service. */
-        public void init() {
-            mNativeServicePtr = VibratorManagerService.nativeInit();
-            long finalizerPtr = VibratorManagerService.nativeGetFinalizer();
+        public void init(OnSyncedVibrationCompleteListener listener) {
+            mNativeServicePtr = nativeInit(listener);
+            long finalizerPtr = nativeGetFinalizer();
 
             if (finalizerPtr != 0) {
                 NativeAllocationRegistry registry =
@@ -964,9 +1017,29 @@
             }
         }
 
+        /** Returns manager capabilities. */
+        public long getCapabilities() {
+            return nativeGetCapabilities(mNativeServicePtr);
+        }
+
         /** Returns vibrator ids. */
         public int[] getVibratorIds() {
-            return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr);
+            return nativeGetVibratorIds(mNativeServicePtr);
+        }
+
+        /** Prepare vibrators for triggering vibrations in sync. */
+        public boolean prepareSynced(@NonNull int[] vibratorIds) {
+            return nativePrepareSynced(mNativeServicePtr, vibratorIds);
+        }
+
+        /** Trigger prepared synced vibration. */
+        public boolean triggerSynced(long vibrationId) {
+            return nativeTriggerSynced(mNativeServicePtr, vibrationId);
+        }
+
+        /** Cancel prepared synced vibration. */
+        public void cancelSynced() {
+            nativeCancelSynced(mNativeServicePtr);
         }
     }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 026eb63..2ac365d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -119,11 +119,17 @@
     private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
 
         @Override
-        public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
+        public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
+            return false;
         }
 
         @Override
-        public void triggerSyncedVibration(long vibrationId) {
+        public boolean triggerSyncedVibration(long vibrationId) {
+            return false;
+        }
+
+        @Override
+        public void cancelSyncedVibration() {
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ecb915f..f0f29a9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -145,6 +145,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityThread;
+import android.app.AnrController;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
@@ -5632,7 +5633,7 @@
             final int modeFlags, int userId) {
         enforceNotIsolatedCaller("grantUriPermission");
         GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
-        synchronized (mProcLock) {
+        synchronized (this) {
             final ProcessRecord r = getRecordForAppLOSP(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
@@ -5666,7 +5667,7 @@
     public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
             final int modeFlags, int userId) {
         enforceNotIsolatedCaller("revokeUriPermission");
-        synchronized (mProcLock) {
+        synchronized (this) {
             final ProcessRecord r = getRecordForAppLOSP(caller);
             if (r == null) {
                 throw new SecurityException("Unable to find app for caller "
@@ -10699,13 +10700,13 @@
             long kernelUsed = memInfo.getKernelUsedSizeKb();
             final long ionHeap = Debug.getIonHeapsSizeKb();
             final long ionPool = Debug.getIonPoolsSizeKb();
+            final long dmabufMapped = Debug.getDmabufMappedSizeKb();
             if (ionHeap >= 0 && ionPool >= 0) {
-                final long ionMapped = Debug.getIonMappedSizeKb();
-                final long ionUnmapped = ionHeap - ionMapped;
+                final long ionUnmapped = ionHeap - dmabufMapped;
                 pw.print("      ION: ");
                         pw.print(stringifyKBSize(ionHeap + ionPool));
                         pw.print(" (");
-                        pw.print(stringifyKBSize(ionMapped));
+                        pw.print(stringifyKBSize(dmabufMapped));
                         pw.print(" mapped + ");
                         pw.print(stringifyKBSize(ionUnmapped));
                         pw.print(" unmapped + ");
@@ -10714,11 +10715,34 @@
                 // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
                 // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
                 kernelUsed += ionHeap;
+            } else {
+                final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+                if (totalExportedDmabuf >= 0) {
+                    final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
+                    pw.print("DMA-BUF: ");
+                    pw.print(stringifyKBSize(totalExportedDmabuf));
+                    pw.print(" (");
+                    pw.print(stringifyKBSize(dmabufMapped));
+                    pw.print(" mapped + ");
+                    pw.print(stringifyKBSize(dmabufUnmapped));
+                    pw.println(" unmapped)");
+                    kernelUsed += totalExportedDmabuf;
+                }
+                final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+                if (totalDmabufHeapPool >= 0) {
+                    pw.print("DMA-BUF Heaps pool: ");
+                    pw.println(stringifyKBSize(totalDmabufHeapPool));
+                }
             }
             final long gpuUsage = Debug.getGpuTotalUsageKb();
             if (gpuUsage >= 0) {
                 pw.print("      GPU: "); pw.println(stringifyKBSize(gpuUsage));
             }
+
+            /*
+             * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+             * memInfo.getCachedSizeKb().
+             */
             final long lostRAM = memInfo.getTotalSizeKb()
                     - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
@@ -15861,6 +15885,16 @@
             // PackageManagerService.
             return mConstants.mBootTimeTempAllowlistDuration;
         }
+
+        @Override
+        public void registerAnrController(AnrController controller) {
+            mActivityTaskManager.registerAnrController(controller);
+        }
+
+        @Override
+        public void unregisterAnrController(AnrController controller) {
+            mActivityTaskManager.unregisterAnrController(controller);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fe71fbf..c971bd2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2984,7 +2984,13 @@
                         pw.println("Reset all changes for " + packageName + " to default value.");
                         return 0;
                     }
-                    if (platformCompat.clearOverride(changeId, packageName)) {
+                    boolean existed;
+                    if (killPackage) {
+                        existed = platformCompat.clearOverride(changeId, packageName);
+                    } else {
+                        existed = platformCompat.clearOverrideForTest(changeId, packageName);
+                    }
+                    if (existed) {
                         pw.println("Reset change " + changeId + " for " + packageName
                                 + " to default value.");
                     } else {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 8f11a5a..3ff5872 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1512,15 +1512,29 @@
         final long ionHeap = Debug.getIonHeapsSizeKb();
         final long ionPool = Debug.getIonPoolsSizeKb();
         if (ionHeap >= 0 && ionPool >= 0) {
-            final long ionMapped = Debug.getIonMappedSizeKb();
-            final long ionUnmapped = ionHeap - ionMapped;
             memInfoBuilder.append("       ION: ");
             memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
             memInfoBuilder.append("\n");
             // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
             // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
             kernelUsed += ionHeap;
+        } else {
+            final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+            if (totalExportedDmabuf >= 0) {
+                memInfoBuilder.append("DMA-BUF: ");
+                memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
+                memInfoBuilder.append("\n");
+                kernelUsed += totalExportedDmabuf;
+            }
+
+            final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+            if (totalDmabufHeapPool >= 0) {
+                memInfoBuilder.append("DMA-BUF Heaps pool: ");
+                memInfoBuilder.append(stringifyKBSize(totalDmabufHeapPool));
+                memInfoBuilder.append("\n");
+            }
         }
+
         final long gpuUsage = Debug.getGpuTotalUsageKb();
         if (gpuUsage >= 0) {
             memInfoBuilder.append("       GPU: ");
@@ -1531,6 +1545,11 @@
         memInfoBuilder.append(stringifyKBSize(
                                   totalPss - cachedPss + kernelUsed));
         memInfoBuilder.append("\n");
+
+        /*
+         * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+         * memInfo.getCachedSizeKb().
+         */
         memInfoBuilder.append("  Lost RAM: ");
         memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
                 - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 773e313..336a595 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -126,15 +126,20 @@
                     .onMalformedInput(CodingErrorAction.REPLACE)
                     .onUnmappableCharacter(CodingErrorAction.REPLACE)
                     .replaceWith("?");
-    private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+    private static final int MAX_LOW_POWER_STATS_SIZE = 8192;
     private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
+    private static final String EMPTY = "Empty";
 
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
     private final Object mLock = new Object();
 
+    private final Object mPowerStatsLock = new Object();
+    @GuardedBy("mPowerStatsLock")
     private PowerStatsInternal mPowerStatsInternal = null;
+    @GuardedBy("mPowerStatsLock")
     private Map<Integer, String> mEntityNames = new HashMap();
+    @GuardedBy("mPowerStatsLock")
     private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
 
     @GuardedBy("mStats")
@@ -172,13 +177,6 @@
             };
 
     private void populatePowerEntityMaps() {
-        if (mPowerStatsInternal == null) {
-            // PowerStatsInternal unavailable, don't bother populating maps.
-            mEntityNames = null;
-            mStateNames = null;
-            return;
-        }
-
         PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
         if (entities == null) {
             return;
@@ -202,6 +200,12 @@
      */
     @Override
     public void fillLowPowerStats(RpmStats rpmStats) {
+        synchronized (mPowerStatsLock) {
+            if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+                return;
+            }
+        }
+
         final StateResidencyResult[] results;
         try {
             results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
@@ -237,16 +241,22 @@
 
     @Override
     public String getSubsystemLowPowerStats() {
+        synchronized (mPowerStatsLock) {
+            if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+                return EMPTY;
+            }
+        }
+
         final StateResidencyResult[] results;
         try {
             results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
                     .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
         } catch (Exception e) {
             Slog.e(TAG, "Failed to getStateResidencyAsync", e);
-            return "Empty";
+            return EMPTY;
         }
 
-        if (results.length == 0) return "Empty";
+        if (results.length == 0) return EMPTY;
 
         int charsLeft = MAX_LOW_POWER_STATS_SIZE;
         StringBuilder builder = new StringBuilder("SubsystemPowerState");
@@ -322,9 +332,14 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
         }
-        mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
-        if (mPowerStatsInternal != null) {
-            populatePowerEntityMaps();
+
+        synchronized (mPowerStatsLock) {
+            mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+            if (mPowerStatsInternal != null) {
+                populatePowerEntityMaps();
+            } else {
+                Slog.e(TAG, "Could not register PowerStatsInternal");
+            }
         }
 
         Watchdog.getInstance().addMonitor(this);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c18031f..7bdf43c 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -538,6 +538,12 @@
     private static native int getBinderFreezeInfo(int pid);
 
     /**
+     * Returns the path to be checked to verify whether the freezer is supported by this system.
+     * @return absolute path to the file
+     */
+    private static native String getFreezerCheckPath();
+
+    /**
      * Determines whether the freezer is supported by this system
      */
     public static boolean isFreezerSupported() {
@@ -545,11 +551,15 @@
         FileReader fr = null;
 
         try {
-            fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze");
+            fr = new FileReader(getFreezerCheckPath());
             char state = (char) fr.read();
 
             if (state == '1' || state == '0') {
                 supported = true;
+                // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to
+                // http://b/179006802.
+                // TODO: remove once the uid/pid hierarchy is restored
+                enableFreezerInternal(true);
             } else {
                 Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
             }
@@ -1149,7 +1159,7 @@
                     }
                     return;
                 }
-            } catch (IOException e) {
+            } catch (Exception e) {
                 Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
                         + "): " + e);
                 return;
@@ -1244,7 +1254,7 @@
                         }
                     }
                 }
-            } catch (IOException e) {
+            } catch (Exception e) {
                 Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
                 synchronized (mAm) {
                     synchronized (mProcLock) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 64e307a..1653123 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -416,6 +416,14 @@
             return;
         }
 
+        // Retrieve max ANR delay from AnrControllers without the mService lock since the
+        // controllers might in turn call into apps
+        long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo);
+        if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) {
+            Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs
+                    + "ms");
+        }
+
         synchronized (mService) {
             // mBatteryStatsService can be null if the AMS is constructed with injector only. This
             // will only happen in tests.
@@ -447,7 +455,7 @@
                 msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
                 msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
 
-                mService.mUiHandler.sendMessage(msg);
+                mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7299e81..e022e97 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@
     static final String[] sDeviceConfigScopes = new String[] {
         DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_CONFIGURATION,
+        DeviceConfig.NAMESPACE_CONNECTIVITY,
         DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3acad49..76c3467 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -16,17 +16,23 @@
 
 package com.android.server.app;
 
+import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.GameManager.GameMode;
 import android.app.IGameManagerService;
 import android.content.Context;
+import android.content.pm.PackageManager;
+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.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -81,6 +87,14 @@
             switch (msg.what) {
                 case WRITE_SETTINGS: {
                     final int userId = (int) msg.obj;
+                    if (userId < 0) {
+                        Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+                        synchronized (mLock) {
+                            removeMessages(WRITE_SETTINGS, msg.obj);
+                        }
+                        break;
+                    }
+
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                     synchronized (mLock) {
                         removeMessages(WRITE_SETTINGS, msg.obj);
@@ -94,6 +108,15 @@
                 }
                 case REMOVE_SETTINGS: {
                     final int userId = (int) msg.obj;
+                    if (userId < 0) {
+                        Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+                        synchronized (mLock) {
+                            removeMessages(WRITE_SETTINGS, msg.obj);
+                            removeMessages(REMOVE_SETTINGS, msg.obj);
+                        }
+                        break;
+                    }
+
                     synchronized (mLock) {
                         // Since the user was removed, ignore previous write message
                         // and do write here.
@@ -146,9 +169,23 @@
         }
     }
 
-    //TODO(b/178111358) Add proper permission check and multi-user handling
+    private boolean hasPermission(String permission) {
+        return mContext.checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @GameMode int getGameMode(String packageName, int userId) {
+        if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+            Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+            return GameManager.GAME_MODE_UNSUPPORTED;
+        }
+
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "getGameMode",
+                "com.android.server.app.GameManagerService");
+
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 return GameManager.GAME_MODE_UNSUPPORTED;
@@ -158,9 +195,18 @@
         }
     }
 
-    //TODO(b/178111358) Add proper permission check and multi-user handling
     @Override
+    @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
+        if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+            Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+            return;
+        }
+
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "setGameMode",
+                "com.android.server.app.GameManagerService");
+
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 return;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9aea7c4..f72fb1f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -765,10 +765,6 @@
         return mAudioService.getVssVolumeForDevice(streamType, device);
     }
 
-    /*package*/ int getModeOwnerPid() {
-        return mModeOwnerPid;
-    }
-
     /*package*/ int getDeviceForStream(int streamType) {
         return mAudioService.getDeviceForStream(streamType);
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1529d71..6f625a7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.MediaMetrics;
+import android.media.MediaRecorder.AudioSource;
 import android.media.PlayerBase;
 import android.media.VolumePolicy;
 import android.media.audiofx.AudioEffect;
@@ -161,9 +162,11 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -300,6 +303,10 @@
     private static final int MSG_STREAM_DEVICES_CHANGED = 32;
     private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
     private static final int MSG_REINIT_VOLUMES = 34;
+    private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
+    private static final int MSG_UPDATE_AUDIO_MODE = 36;
+    private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
+
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +706,9 @@
     private long mLoweredFromNormalToVibrateTime;
 
     // Array of Uids of valid accessibility services to check if caller is one of them
-    private int[] mAccessibilityServiceUids;
     private final Object mAccessibilityServiceUidsLock = new Object();
+    @GuardedBy("mAccessibilityServiceUidsLock")
+    private int[] mAccessibilityServiceUids;
 
     // Uid of the active input method service to check if caller is the one or not.
     private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -939,6 +947,7 @@
         mDeviceBroker = new AudioDeviceBroker(mContext, this);
 
         mRecordMonitor = new RecordingActivityMonitor(mContext);
+        mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
 
         // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
         // array initialized by updateStreamVolumeAlias()
@@ -948,6 +957,8 @@
 
         mPlaybackMonitor =
                 new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+        mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true);
+
         mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
 
         readAndSetLowRamDevice();
@@ -1196,12 +1207,8 @@
 
         // Restore call state
         synchronized (mDeviceBroker.mSetModeLock) {
-            if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
-                    ==  AudioSystem.AUDIO_STATUS_OK) {
-                mModeLogger.log(new AudioEventLogger.StringEvent(
-                        "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
-                        + ", uid=" + getModeOwnerUid() + ")"));
-            }
+            onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
+                    mContext.getPackageName());
         }
         final int forSys;
         synchronized (mSettingsLock) {
@@ -2989,7 +2996,7 @@
     }
 
     /*package*/ int getHearingAidStreamType() {
-        return getHearingAidStreamType(mMode);
+        return getHearingAidStreamType(getMode());
     }
 
     private int getHearingAidStreamType(int mode) {
@@ -3002,15 +3009,15 @@
                 // other conditions will influence the stream type choice, read on...
                 break;
         }
-        if (mVoiceActive.get()) {
+        if (mVoicePlaybackActive.get()) {
             return AudioSystem.STREAM_VOICE_CALL;
         }
         return AudioSystem.STREAM_MUSIC;
     }
 
-    private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+    private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
 
-    private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+    private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor =
             new IPlaybackConfigDispatcher.Stub() {
         @Override
         public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
@@ -3032,16 +3039,126 @@
                 break;
             }
         }
-        if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+        if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) {
             updateHearingAidVolumeOnVoiceActivityUpdate();
         }
+
+        // Update playback active state for all apps in audio mode stack.
+        // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+        // and request an audio mode update immediately. Upon any other change, queue the message
+        // and request an audio mode update after a grace period.
+        synchronized (mDeviceBroker.mSetModeLock) {
+            boolean updateAudioMode = false;
+            int existingMsgPolicy = SENDMSG_QUEUE;
+            int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+            for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                boolean wasActive = h.isActive();
+                h.setPlaybackActive(false);
+                for (AudioPlaybackConfiguration config : configs) {
+                    final int usage = config.getAudioAttributes().getUsage();
+                    if (config.getClientUid() == h.getUid()
+                            && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+                                || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+                            && config.getPlayerState()
+                                == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+                        h.setPlaybackActive(true);
+                        break;
+                    }
+                }
+                if (wasActive != h.isActive()) {
+                    updateAudioMode = true;
+                    if (h.isActive() && h == getAudioModeOwnerHandler()) {
+                        existingMsgPolicy = SENDMSG_REPLACE;
+                        delay = 0;
+                    }
+                }
+            }
+            if (updateAudioMode) {
+                sendMsg(mAudioHandler,
+                        MSG_UPDATE_AUDIO_MODE,
+                        existingMsgPolicy,
+                        AudioSystem.MODE_CURRENT,
+                        android.os.Process.myPid(),
+                        mContext.getPackageName(),
+                        delay);
+            }
+        }
+    }
+
+    private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
+            new IRecordingConfigDispatcher.Stub() {
+        @Override
+        public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+            sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE,
+                    0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+                    configs /*obj*/, 0 /*delay*/);
+        }
+    };
+
+    private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+        // Update recording active state for all apps in audio mode stack.
+        // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+        // and request an audio mode update immediately. Upon any other change, queue the message
+        // and request an audio mode update after a grace period.
+        synchronized (mDeviceBroker.mSetModeLock) {
+            boolean updateAudioMode = false;
+            int existingMsgPolicy = SENDMSG_QUEUE;
+            int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+            for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                boolean wasActive = h.isActive();
+                h.setRecordingActive(false);
+                for (AudioRecordingConfiguration config : configs) {
+                    if (config.getClientUid() == h.getUid()
+                            && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
+                        h.setRecordingActive(true);
+                        break;
+                    }
+                }
+                if (wasActive != h.isActive()) {
+                    updateAudioMode = true;
+                    if (h.isActive() && h == getAudioModeOwnerHandler()) {
+                        existingMsgPolicy = SENDMSG_REPLACE;
+                        delay = 0;
+                    }
+                }
+            }
+            if (updateAudioMode) {
+                sendMsg(mAudioHandler,
+                        MSG_UPDATE_AUDIO_MODE,
+                        existingMsgPolicy,
+                        AudioSystem.MODE_CURRENT,
+                        android.os.Process.myPid(),
+                        mContext.getPackageName(),
+                        delay);
+            }
+        }
+    }
+
+    private void dumpAudioMode(PrintWriter pw) {
+        pw.println("\nAudio mode: ");
+        pw.println("- Current mode = " + AudioSystem.modeToString(getMode()));
+        pw.println("- Mode owner: ");
+        SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+        if (hdlr != null) {
+            hdlr.dump(pw, -1);
+        } else {
+            pw.println("   None");
+        }
+        pw.println("- Mode owner stack: ");
+        if (mSetModeDeathHandlers.isEmpty()) {
+            pw.println("   Empty");
+        } else {
+            for (int i = 0; i < mSetModeDeathHandlers.size(); i++) {
+                mSetModeDeathHandlers.get(i).dump(pw, i);
+            }
+        }
     }
 
     private void updateHearingAidVolumeOnVoiceActivityUpdate() {
         final int streamType = getHearingAidStreamType();
         final int index = getStreamVolume(streamType);
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
-                mVoiceActive.get(), streamType, index));
+                mVoicePlaybackActive.get(), streamType, index));
         mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
 
     }
@@ -4058,65 +4175,46 @@
 
     }
 
-    /**
-     * Return the pid of the current audio mode owner
-     * @return 0 if nobody owns the mode
-     */
-    /*package*/ int getModeOwnerPid() {
-        int modeOwnerPid = 0;
-        try {
-            modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
-        } catch (Exception e) {
-            // nothing to do, modeOwnerPid is not modified
-        }
-        return modeOwnerPid;
-    }
-
-    /**
-     * Return the uid of the current audio mode owner
-     * @return 0 if nobody owns the mode
-     */
-    /*package*/ int getModeOwnerUid() {
-        int modeOwnerUid = 0;
-        try {
-            modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
-        } catch (Exception e) {
-            // nothing to do, modeOwnerUid is not modified
-        }
-        return modeOwnerUid;
-    }
-
     private class SetModeDeathHandler implements IBinder.DeathRecipient {
         private final IBinder mCb; // To be notified of client's death
         private final int mPid;
         private final int mUid;
         private final boolean mIsPrivileged;
         private final String mPackage;
-        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
+        private int mMode;
+        private long mUpdateTime;
+        private boolean mPlaybackActive = false;
+        private boolean mRecordingActive = false;
 
-        SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+        SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged,
+                            String caller, int mode) {
+            mMode = mode;
             mCb = cb;
             mPid = pid;
             mUid = uid;
-            mIsPrivileged = isPrivileged;
             mPackage = caller;
+            mIsPrivileged = isPrivileged;
+            mUpdateTime = java.lang.System.currentTimeMillis();
         }
 
         public void binderDied() {
-            int newModeOwnerPid = 0;
             synchronized (mDeviceBroker.mSetModeLock) {
-                Log.w(TAG, "setMode() client died");
+                Log.w(TAG, "SetModeDeathHandler client died");
                 int index = mSetModeDeathHandlers.indexOf(this);
                 if (index < 0) {
-                    Log.w(TAG, "unregistered setMode() client died");
+                    Log.w(TAG, "unregistered SetModeDeathHandler client died");
                 } else {
-                    newModeOwnerPid = setModeInt(
-                            AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
+                    SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
+                    mSetModeDeathHandlers.remove(index);
+                    sendMsg(mAudioHandler,
+                            MSG_UPDATE_AUDIO_MODE,
+                            SENDMSG_QUEUE,
+                            AudioSystem.MODE_CURRENT,
+                            android.os.Process.myPid(),
+                            mContext.getPackageName(),
+                            0);
                 }
             }
-            // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
-            // SCO connections not started by the application changing the mode when pid changes
-            mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
         }
 
         public int getPid() {
@@ -4125,6 +4223,7 @@
 
         public void setMode(int mode) {
             mMode = mode;
+            mUpdateTime = java.lang.System.currentTimeMillis();
         }
 
         public int getMode() {
@@ -4146,25 +4245,131 @@
         public boolean isPrivileged() {
             return mIsPrivileged;
         }
+
+        public long getUpdateTime() {
+            return mUpdateTime;
+        }
+
+        public void setPlaybackActive(boolean active) {
+            mPlaybackActive = active;
+        }
+
+        public void setRecordingActive(boolean active) {
+            mRecordingActive = active;
+        }
+
+        /**
+         * An app is considered active if:
+         * - It is privileged (has MODIFY_PHONE_STATE permission)
+         *  or
+         * - It requests mode MODE_IN_COMMUNICATION, and it is either playing
+         * or recording for VOICE_COMMUNICATION.
+         *   or
+         * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL
+         */
+        public boolean isActive() {
+            return mIsPrivileged
+                    || ((mMode == AudioSystem.MODE_IN_COMMUNICATION)
+                        && (mRecordingActive || mPlaybackActive))
+                    || mMode == AudioSystem.MODE_IN_CALL
+                    || mMode == AudioSystem.MODE_RINGTONE
+                    || mMode == AudioSystem.MODE_CALL_SCREENING;
+        }
+
+        public void dump(PrintWriter pw, int index) {
+            SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+            if (index >= 0) {
+                pw.println("  Requester # " + (index + 1) + ":");
+            }
+            pw.println("  - Mode: " + AudioSystem.modeToString(mMode));
+            pw.println("  - Binder: " + mCb);
+            pw.println("  - Pid: " + mPid);
+            pw.println("  - Uid: " + mUid);
+            pw.println("  - Package: " + mPackage);
+            pw.println("  - Privileged: " + mIsPrivileged);
+            pw.println("  - Active: " + isActive());
+            pw.println("    Playback active: " + mPlaybackActive);
+            pw.println("    Recording active: " + mRecordingActive);
+            pw.println("  - update time: " + format.format(new Date(mUpdateTime)));
+        }
+    }
+
+    @GuardedBy("mDeviceBroker.mSetModeLock")
+    private SetModeDeathHandler getAudioModeOwnerHandler() {
+        // The Audio mode owner is:
+        // 1) the most recent privileged app in the stack
+        // 2) the most recent active app in the tack
+        SetModeDeathHandler modeOwner = null;
+        SetModeDeathHandler privilegedModeOwner = null;
+        for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+            if (h.isActive()) {
+                // privileged apps are always active
+                if (h.isPrivileged()) {
+                    if (privilegedModeOwner == null
+                            || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) {
+                        privilegedModeOwner = h;
+                    }
+                } else {
+                    if (modeOwner == null
+                            || h.getUpdateTime() > modeOwner.getUpdateTime()) {
+                        modeOwner = h;
+                    }
+                }
+            }
+        }
+        return privilegedModeOwner != null ? privilegedModeOwner :  modeOwner;
+    }
+
+    /**
+     * Return the pid of the current audio mode owner
+     * @return 0 if nobody owns the mode
+     */
+    @GuardedBy("mDeviceBroker.mSetModeLock")
+    /*package*/ int getModeOwnerPid() {
+        SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+        if (hdlr != null) {
+            return hdlr.getPid();
+        }
+        return 0;
+    }
+
+    /**
+     * Return the uid of the current audio mode owner
+     * @return 0 if nobody owns the mode
+     */
+    @GuardedBy("mDeviceBroker.mSetModeLock")
+    /*package*/ int getModeOwnerUid() {
+        SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+        if (hdlr != null) {
+            return hdlr.getUid();
+        }
+        return 0;
     }
 
     /** @see AudioManager#setMode(int) */
     public void setMode(int mode, IBinder cb, String callingPackage) {
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
         if (DEBUG_MODE) {
-            Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+            Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid
+                    + ", uid=" + uid + ", caller=" + callingPackage + ")");
         }
         if (!checkAudioSettingsPermission("setMode()")) {
             return;
         }
-        final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.MODIFY_PHONE_STATE)
-                        == PackageManager.PERMISSION_GRANTED;
-        final int callingPid = Binder.getCallingPid();
-        if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
-            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
-                    + callingPid + ", uid=" + Binder.getCallingUid());
+        if (cb == null) {
+            Log.e(TAG, "setMode() called with null binder");
             return;
         }
+        if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+            Log.w(TAG, "setMode() invalid mode: " + mode);
+            return;
+        }
+
+        if (mode == AudioSystem.MODE_CURRENT) {
+            mode = getMode();
+        }
 
         if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) {
             Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted "
@@ -4172,171 +4377,152 @@
             return;
         }
 
-        if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+        final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                == PackageManager.PERMISSION_GRANTED;
+        if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
+            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+                    + pid + ", uid=" + Binder.getCallingUid());
             return;
         }
 
-        int newModeOwnerPid;
+        SetModeDeathHandler currentModeHandler = null;
         synchronized (mDeviceBroker.mSetModeLock) {
-            if (mode == AudioSystem.MODE_CURRENT) {
-                mode = mMode;
+            for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                if (h.getPid() == pid) {
+                    currentModeHandler = h;
+                    break;
+                }
             }
-            int oldModeOwnerPid = getModeOwnerPid();
-            // Do not allow changing mode if a call is active and the requester
-            // does not have permission to modify phone state or is not the mode owner,
-            // unless returning to NORMAL mode (will not change current mode owner) or
-            // not changing mode in which case the mode owner will reflect the last
-            // requester of current mode
-            if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL))
-                    && ((mMode == AudioSystem.MODE_IN_CALL)
-                        || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
-                    && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
-                Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
-                        + ", uid=" + Binder.getCallingUid()
-                        + ", cannot change mode from " + mMode
-                        + " without permission or being mode owner");
-                return;
+
+            if (mode == AudioSystem.MODE_NORMAL) {
+                if (currentModeHandler != null) {
+                    if (!currentModeHandler.isPrivileged()
+                            && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
+                        mAudioHandler.removeEqualMessages(
+                                MSG_CHECK_MODE_FOR_UID, currentModeHandler);
+                    }
+                    mSetModeDeathHandlers.remove(currentModeHandler);
+                    if (DEBUG_MODE) {
+                        Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid);
+                    }
+                    try {
+                        currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0);
+                    } catch (NoSuchElementException e) {
+                        Log.w(TAG, "setMode link does not exist ...");
+                    }
+                }
+            } else {
+                if (currentModeHandler != null) {
+                    currentModeHandler.setMode(mode);
+                    if (DEBUG_MODE) {
+                        Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid);
+                    }
+                } else {
+                    currentModeHandler = new SetModeDeathHandler(cb, pid, uid,
+                            hasModifyPhoneStatePermission, callingPackage, mode);
+                    // Register for client death notification
+                    try {
+                        cb.linkToDeath(currentModeHandler, 0);
+                    } catch (RemoteException e) {
+                        // Client has died!
+                        Log.w(TAG, "setMode() could not link to " + cb + " binder death");
+                        return;
+                    }
+                    mSetModeDeathHandlers.add(currentModeHandler);
+                    if (DEBUG_MODE) {
+                        Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid);
+                    }
+                }
+                if (mode == AudioSystem.MODE_IN_COMMUNICATION) {
+                    // Force active state when entering/updating the stack to avoid glitches when
+                    // an app starts playing/recording after settng the audio mode,
+                    // and send a reminder to check activity after a grace period.
+                    if (!currentModeHandler.isPrivileged()) {
+                        currentModeHandler.setPlaybackActive(true);
+                        currentModeHandler.setRecordingActive(true);
+                        sendMsg(mAudioHandler,
+                                MSG_CHECK_MODE_FOR_UID,
+                                SENDMSG_QUEUE,
+                                0,
+                                0,
+                                currentModeHandler,
+                                CHECK_MODE_FOR_UID_PERIOD_MS);
+                    }
+                }
             }
-            newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
-                    hasModifyPhoneStatePermission, callingPackage);
+
+            sendMsg(mAudioHandler,
+                    MSG_UPDATE_AUDIO_MODE,
+                    SENDMSG_REPLACE,
+                    mode,
+                    pid,
+                    callingPackage,
+                    0);
         }
-        // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
-        // SCO connections not started by the application changing the mode when pid changes
-        mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
     }
 
-    // setModeInt() returns a valid PID if the audio mode was successfully set to
-    // any mode other than NORMAL.
     @GuardedBy("mDeviceBroker.mSetModeLock")
-    private int setModeInt(
-            int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+    void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) {
+        if (requestedMode == AudioSystem.MODE_CURRENT) {
+            requestedMode = getMode();
+        }
+        int mode = AudioSystem.MODE_NORMAL;
+        int uid = 0;
+        int pid = 0;
+        SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+        if (currentModeHandler != null) {
+            mode = currentModeHandler.getMode();
+            uid = currentModeHandler.getUid();
+            pid = currentModeHandler.getPid();
+        }
         if (DEBUG_MODE) {
-            Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
-                    + ", uid=" + uid + ", caller=" + caller + ")");
+            Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode
+                    + " requestedMode: " + requestedMode);
         }
-        int newModeOwnerPid = 0;
-        if (cb == null) {
-            Log.e(TAG, "setModeInt() called with null binder");
-            return newModeOwnerPid;
-        }
+        if (mode != mMode) {
+            final long identity = Binder.clearCallingIdentity();
+            int status = mAudioSystem.setPhoneState(mode, uid);
+            Binder.restoreCallingIdentity(identity);
+            if (status == AudioSystem.AUDIO_STATUS_OK) {
+                if (DEBUG_MODE) {
+                    Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
+                }
+                int previousMode = mMode;
+                mMode = mode;
+                // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+                mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
+                        requestedMode, pid, mode));
 
-        SetModeDeathHandler hdlr = null;
-        Iterator iter = mSetModeDeathHandlers.iterator();
-        while (iter.hasNext()) {
-            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
-            if (h.getPid() == pid) {
-                hdlr = h;
-                // Remove from client list so that it is re-inserted at top of list
-                iter.remove();
-                if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
-                    mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr);
-                }
-                try {
-                    hdlr.getBinder().unlinkToDeath(hdlr, 0);
-                    if (cb != hdlr.getBinder()){
-                        hdlr = null;
-                    }
-                } catch (NoSuchElementException e) {
-                    hdlr = null;
-                    Log.w(TAG, "link does not exist ...");
-                }
-                break;
-            }
-        }
-        final int oldMode = mMode;
-        int status = AudioSystem.AUDIO_STATUS_OK;
-        int actualMode;
-        do {
-            actualMode = mode;
-            if (mode == AudioSystem.MODE_NORMAL) {
-                // get new mode from client at top the list if any
-                if (!mSetModeDeathHandlers.isEmpty()) {
-                    hdlr = mSetModeDeathHandlers.get(0);
-                    cb = hdlr.getBinder();
-                    actualMode = hdlr.getMode();
-                    if (DEBUG_MODE) {
-                        Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
-                                + hdlr.mPid);
-                    }
-                }
+                int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+                int device = getDeviceForStream(streamType);
+                int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
+                setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true,
+                        requesterPackage, true /*hasModifyAudioSettings*/);
+
+                updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage);
+
+                // change of mode may require volume to be re-applied on some devices
+                updateAbsVolumeMultiModeDevices(previousMode, mode);
+
+                // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+                // connections not started by the application changing the mode when pid changes
+                mDeviceBroker.postSetModeOwnerPid(pid, mode);
             } else {
-                if (hdlr == null) {
-                    hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
-                }
-                // Register for client death notification
-                try {
-                    cb.linkToDeath(hdlr, 0);
-                } catch (RemoteException e) {
-                    // Client has died!
-                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
-                }
-
-                // Last client to call setMode() is always at top of client list
-                // as required by SetModeDeathHandler.binderDied()
-                mSetModeDeathHandlers.add(0, hdlr);
-                hdlr.setMode(mode);
-            }
-
-            if (actualMode != mMode) {
-                final long identity = Binder.clearCallingIdentity();
-                status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
-                Binder.restoreCallingIdentity(identity);
-                if (status == AudioSystem.AUDIO_STATUS_OK) {
-                    if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
-                    mMode = actualMode;
-                } else {
-                    if (hdlr != null) {
-                        mSetModeDeathHandlers.remove(hdlr);
-                        cb.unlinkToDeath(hdlr, 0);
-                    }
-                    // force reading new top of mSetModeDeathHandlers stack
-                    if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); }
-                    mode = AudioSystem.MODE_NORMAL;
-                }
-            } else {
-                status = AudioSystem.AUDIO_STATUS_OK;
-            }
-        } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
-
-        if (status == AudioSystem.AUDIO_STATUS_OK) {
-            if (actualMode != AudioSystem.MODE_NORMAL) {
-                newModeOwnerPid = getModeOwnerPid();
-                if (newModeOwnerPid == 0) {
-                    Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
-                }
-            }
-            // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
-            mModeLogger.log(
-                    new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
-
-            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
-            int device = getDeviceForStream(streamType);
-            int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
-            setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller,
-                    true /*hasModifyAudioSettings*/);
-
-            updateStreamVolumeAlias(true /*updateVolumes*/, caller);
-
-            // change of mode may require volume to be re-applied on some devices
-            updateAbsVolumeMultiModeDevices(oldMode, actualMode);
-
-            if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
-                    && !hdlr.isPrivileged()) {
-                sendMsg(mAudioHandler,
-                        MSG_CHECK_MODE_FOR_UID,
-                        SENDMSG_QUEUE,
-                        0,
-                        0,
-                        hdlr,
-                        CHECK_MODE_FOR_UID_PERIOD_MS);
+                Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
             }
         }
-        return newModeOwnerPid;
     }
 
     /** @see AudioManager#getMode() */
     public int getMode() {
-        return mMode;
+        synchronized (mDeviceBroker.mSetModeLock) {
+            SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+            if (currentModeHandler != null) {
+                return currentModeHandler.getMode();
+            }
+            return AudioSystem.MODE_NORMAL;
+        }
     }
 
     /** cached value read from audiopolicy manager after initialization. */
@@ -5683,11 +5869,6 @@
             throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
                     + " (dis)connection, got " + state);
         }
-        if (state == BluetoothProfile.STATE_CONNECTED) {
-            mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
-        } else {
-            mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
-        }
         mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
                 device, state, suppressNoisyIntent, musicDevice, "AudioService");
     }
@@ -7032,6 +7213,9 @@
                 case MSG_PLAYBACK_CONFIG_CHANGE:
                     onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
                     break;
+                case MSG_RECORDING_CONFIG_CHANGE:
+                    onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj);
+                    break;
 
                 case MSG_BROADCAST_MICROPHONE_MUTE:
                     mSystemServer.sendMicrophoneMuteChangedIntent();
@@ -7042,30 +7226,19 @@
                         if (msg.obj == null) {
                             break;
                         }
-                        // If no other app is currently owning the audio mode and
-                        // the app corresponding to this mode death handler object is still in the
-                        // mode owner stack but not capturing or playing audio after 3 seconds,
-                        // remove it from the stack.
-                        // Otherwise, check again in 3 seconds.
+                        // Update active playback/recording for apps requesting IN_COMMUNICATION
+                        // mode after a grace period following the mode change
                         SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
                         if (mSetModeDeathHandlers.indexOf(h) < 0) {
                             break;
                         }
-                        if (getModeOwnerUid() != h.getUid()
-                                || mRecordMonitor.isRecordingActiveForUid(h.getUid())
-                                || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
-                            sendMsg(mAudioHandler,
-                                    MSG_CHECK_MODE_FOR_UID,
-                                    SENDMSG_QUEUE,
-                                    0,
-                                    0,
-                                    h,
-                                    CHECK_MODE_FOR_UID_PERIOD_MS);
-                            break;
+                        boolean wasActive = h.isActive();
+                        h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
+                        h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
+                        if (wasActive != h.isActive()) {
+                            onUpdateAudioMode(AudioSystem.MODE_CURRENT,
+                                    android.os.Process.myPid(), mContext.getPackageName());
                         }
-                        setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
-                                h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
-                        mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
                     }
                     break;
 
@@ -7082,6 +7255,16 @@
                 case MSG_REINIT_VOLUMES:
                     onReinitVolumes((String) msg.obj);
                     break;
+
+                case MSG_UPDATE_A11Y_SERVICE_UIDS:
+                    onUpdateAccessibilityServiceUids();
+                    break;
+
+                case MSG_UPDATE_AUDIO_MODE:
+                    synchronized (mDeviceBroker.mSetModeLock) {
+                        onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj);
+                    }
+                    break;
             }
         }
     }
@@ -8112,6 +8295,7 @@
         dumpStreamStates(pw);
         dumpVolumeGroups(pw);
         dumpRingerMode(pw);
+        dumpAudioMode(pw);
         pw.println("\nAudio routes:");
         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(
                 mDeviceBroker.getCurAudioRoutes().mainType));
@@ -8148,6 +8332,9 @@
                         + " FromRestrictions=" + mMicMuteFromRestrictions
                         + " FromApi=" + mMicMuteFromApi
                         + " from system=" + mMicMuteFromSystemCached);
+        pw.print("\n  mAssistantUid="); pw.println(mAssistantUid);
+        pw.print("  mCurrentImeUid="); pw.println(mCurrentImeUid);
+        dumpAccessibilityServiceUids(pw);
 
         dumpAudioPolicies(pw);
         mDynPolicyLogger.dump(pw);
@@ -8181,6 +8368,19 @@
         }
     }
 
+    private void dumpAccessibilityServiceUids(PrintWriter pw) {
+        synchronized (mSupportedSystemUsagesLock) {
+            if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+                pw.println("  Accessibility service Uids:");
+                for (int uid : mAccessibilityServiceUids) {
+                    pw.println("  - " + uid);
+                }
+            } else {
+                pw.println("  No accessibility service Uids.");
+            }
+        }
+    }
+
     /**
      * Audio Analytics ids.
      */
@@ -8484,7 +8684,8 @@
                         mAccessibilityServiceUids = uids.toArray();
                     }
                 }
-                AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+                sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+                        0, 0, null, 0);
             }
         }
 
@@ -8502,6 +8703,14 @@
         }
     }
 
+    private void onUpdateAccessibilityServiceUids() {
+        int[] accessibilityServiceUids;
+        synchronized (mAccessibilityServiceUidsLock) {
+            accessibilityServiceUids = mAccessibilityServiceUids;
+        }
+        AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+    }
+
     //==========================================================================================
     // Audio policy management
     //==========================================================================================
@@ -9057,6 +9266,18 @@
     }
 
     /**
+     * Update player session ID
+     * @param piid Player id to update
+     * @param sessionId The new audio session ID
+     */
+    public void playerSessionId(int piid, int sessionId) {
+        if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) {
+            throw new IllegalArgumentException("invalid session Id " + sessionId);
+        }
+        mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid());
+    }
+
+    /**
      * Update player event
      * @param piid Player id to update
      * @param event The new player event
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cd..68a084e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -197,6 +197,28 @@
         }
     }
 
+    /**
+     * Update player session ID
+     * @param piid Player id to update
+     * @param sessionId The new audio session ID
+     * @param binderUid Calling binder uid
+     */
+    public void playerSessionId(int piid, int sessionId, int binderUid) {
+        final boolean change;
+        synchronized (mPlayerLock) {
+            final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
+            if (checkConfigurationCaller(piid, apc, binderUid)) {
+                change = apc.handleSessionIdEvent(sessionId);
+            } else {
+                Log.e(TAG, "Error updating audio session");
+                change = false;
+            }
+        }
+        if (change) {
+            dispatchPlaybackChange(false);
+        }
+    }
+
     private static final int FLAGS_FOR_SILENCE_OVERRIDE =
             AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
             AudioAttributes.FLAG_BYPASS_MUTE;
@@ -921,6 +943,7 @@
         private final int mClientUid;
         private final int mClientPid;
         private final AudioAttributes mPlayerAttr;
+        private final int mSessionId;
 
         NewPlayerEvent(AudioPlaybackConfiguration apc) {
             mPlayerIId = apc.getPlayerInterfaceId();
@@ -928,6 +951,7 @@
             mClientUid = apc.getClientUid();
             mClientPid = apc.getClientPid();
             mPlayerAttr = apc.getAudioAttributes();
+            mSessionId = apc.getSessionId();
         }
 
         @Override
@@ -935,7 +959,8 @@
             return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
                     + mClientPid + " type:"
                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
-                    + " attr:" + mPlayerAttr);
+                    + " attr:" + mPlayerAttr
+                    + " session:" + mSessionId);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 14292d9c..c956043 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -686,7 +686,8 @@
      * @return true if this AuthSession is finished, e.g. should be set to null
      */
     boolean onCancelAuthSession(boolean force) {
-        final boolean authStarted = mState == STATE_AUTH_STARTED
+        final boolean authStarted = mState == STATE_AUTH_CALLED
+                || mState == STATE_AUTH_STARTED
                 || mState == STATE_AUTH_STARTED_UI_SHOWING;
 
         if (authStarted && !force) {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 6905b3d..6851d71 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -90,6 +90,7 @@
             int userId, PromptInfo promptInfo, String opPackageName,
             boolean checkDevicePolicyManager)
             throws RemoteException {
+
         final boolean confirmationRequested = promptInfo.isConfirmationRequested();
         final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
         final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
@@ -111,7 +112,7 @@
 
                 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
                         devicePolicyManager, settingObserver, sensor, userId, opPackageName,
-                        checkDevicePolicyManager, requestedStrength);
+                        checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId());
 
                 Slog.d(TAG, "Package: " + opPackageName
                         + " Sensor ID: " + sensor.id
@@ -141,7 +142,11 @@
             DevicePolicyManager devicePolicyManager,
             BiometricService.SettingObserver settingObserver,
             BiometricSensor sensor, int userId, String opPackageName,
-            boolean checkDevicePolicyManager, int requestedStrength) {
+            boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) {
+
+        if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) {
+            return BIOMETRIC_NO_HARDWARE;
+        }
 
         final boolean wasStrongEnough =
                 Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index d87af42..5cd0bbf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -399,10 +399,15 @@
         }
     }
 
-    public static boolean isKeyguard(Context context, String clientPackage) {
-        final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
-                == PackageManager.PERMISSION_GRANTED;
-
+    /**
+     * Checks if a client package matches Keyguard and can perform internal biometric operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against Keyguard.
+     * @return Whether the given package matches Keyguard.
+     */
+    public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) {
+        final boolean hasPermission = hasInternalPermission(context);
         final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                 context.getResources().getString(R.string.config_keyguardComponent));
         final String keyguardPackage = keyguardComponent != null
@@ -410,6 +415,34 @@
         return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
     }
 
+    /**
+     * Checks if a client package matches the Android system and can perform internal biometric
+     * operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against the Android system.
+     * @return Whether the given package matches the Android system.
+     */
+    public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+        return hasInternalPermission(context) && "android".equals(clientPackage);
+    }
+
+    /**
+     * Checks if a client package matches Settings and can perform internal biometric operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against Settings.
+     * @return Whether the given package matches Settings.
+     */
+    public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) {
+        return hasInternalPermission(context) && "com.android.settings".equals(clientPackage);
+    }
+
+    private static boolean hasInternalPermission(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     public static String getClientName(@Nullable BaseClientMonitor client) {
         return client != null ? client.getClass().getSimpleName() : "null";
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 14433fb..b31a54b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -149,9 +149,10 @@
                 pm.incrementAuthForUser(getTargetUserId(), authenticated);
             }
 
-            // Ensure authentication only succeeds if the client activity is on top or is keyguard.
+            // Ensure authentication only succeeds if the client activity is on top.
             boolean isBackgroundAuth = false;
-            if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
+            if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())
+                    && !Utils.isSystem(getContext(), getOwnerString())) {
                 final List<ActivityManager.RunningTaskInfo> tasks =
                         mActivityTaskManager.getTasks(1);
                 if (tasks == null || tasks.isEmpty()) {
@@ -166,7 +167,7 @@
                         final String topPackage = topActivity.getPackageName();
                         if (!topPackage.contentEquals(getOwnerString())) {
                             Slog.e(TAG, "Background authentication detected, top: " + topPackage
-                                    + ", client: " + this);
+                                    + ", client: " + getOwnerString());
                             isBackgroundAuth = true;
                         }
                     }
@@ -310,4 +311,9 @@
     public int getProtoEnum() {
         return BiometricsProto.CM_AUTHENTICATE;
     }
+
+    @Override
+    public boolean interruptsPrecedingClients() {
+        return true;
+    }
 }
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 8fa3bbb..81ce2d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -82,12 +82,18 @@
     @NonNull protected Callback mCallback;
 
     /**
-     * Returns a ClientMonitorEnum constant defined in biometrics.proto
-     * @return
+     * @return A ClientMonitorEnum constant defined in biometrics.proto
      */
     public abstract int getProtoEnum();
 
     /**
+     * @return True if the ClientMonitor should cancel any current and pending interruptable clients
+     */
+    public boolean interruptsPrecedingClients() {
+        return false;
+    }
+
+    /**
      * @param context    system_server context
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index c5237ab..20c25c3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -475,14 +475,17 @@
      */
     public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
             @Nullable BaseClientMonitor.Callback clientCallback) {
-        // Mark any interruptable pending clients as canceling. Once they reach the head of the
-        // queue, the scheduler will send ERROR_CANCELED and skip the operation.
-        for (Operation operation : mPendingOperations) {
-            if (operation.mClientMonitor instanceof Interruptable
-                    && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
-                Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
-                        + operation.mClientMonitor);
-                operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+        // If the incoming operation should interrupt preceding clients, mark any interruptable
+        // pending clients as canceling. Once they reach the head of the queue, the scheduler will
+        // send ERROR_CANCELED and skip the operation.
+        if (clientMonitor.interruptsPrecedingClients()) {
+            for (Operation operation : mPendingOperations) {
+                if (operation.mClientMonitor instanceof Interruptable
+                        && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+                    Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
+                            + operation.mClientMonitor);
+                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+                }
             }
         }
 
@@ -490,8 +493,11 @@
         Slog.d(getTag(), "[Added] " + clientMonitor
                 + ", new queue size: " + mPendingOperations.size());
 
-        // If the current operation is cancellable, start the cancellation process.
-        if (mCurrentOperation != null && mCurrentOperation.mClientMonitor instanceof Interruptable
+        // If the new operation should interrupt preceding clients, and if the current operation is
+        // cancellable, start the cancellation process.
+        if (clientMonitor.interruptsPrecedingClients()
+                && mCurrentOperation != null
+                && mCurrentOperation.mClientMonitor instanceof Interruptable
                 && mCurrentOperation.mState == Operation.STATE_STARTED) {
             Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
             cancelInternal(mCurrentOperation);
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index da76af8..25b7add 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -16,9 +16,12 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
 import android.hardware.face.IFaceServiceReceiver;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -174,4 +177,33 @@
             mFingerprintServiceReceiver.onUdfpsPointerUp(sensorId);
         }
     }
+
+    // Face-specific callbacks for FaceManager only
+
+    /**
+     * Called each time a new frame is received during face authentication.
+     *
+     * @param frame Information about the current frame.
+     *
+     * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+     */
+    public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame)
+            throws RemoteException {
+        if (mFaceServiceReceiver != null) {
+            mFaceServiceReceiver.onAuthenticationFrame(frame);
+        }
+    }
+
+    /**
+     * Called each time a new frame is received during face enrollment.
+     *
+     * @param frame Information about the current frame.
+     *
+     * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+     */
+    public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) throws RemoteException {
+        if (mFaceServiceReceiver != null) {
+            mFaceServiceReceiver.onEnrollmentFrame(frame);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 8d81016..e1320d8e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -113,4 +113,9 @@
     public int getProtoEnum() {
         return BiometricsProto.CM_ENROLL;
     }
+
+    @Override
+    public boolean interruptsPrecedingClients() {
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index ce24e5e..de57186 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -69,6 +69,8 @@
             final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
                     ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
 
+            Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success);
+
             if (!unknownHALTemplates.isEmpty()) {
                 Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
             }
@@ -90,6 +92,7 @@
     private final Callback mRemoveCallback = new Callback() {
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+            Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
             mCallback.onClientFinished(InternalCleanupClient.this, success);
         }
     };
@@ -115,6 +118,8 @@
     }
 
     private void startCleanupUnknownHalTemplates() {
+        Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size());
+
         UserTemplate template = mUnknownHALTemplates.get(0);
         mUnknownHALTemplates.remove(template);
         mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
@@ -138,6 +143,8 @@
         // Start enumeration. Removal will start if necessary, when enumeration is completed.
         mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
                 getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+
+        Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
         mCurrentTask.start(mEnumerateCallback);
     }
 
@@ -165,6 +172,7 @@
                     + mCurrentTask.getClass().getSimpleName());
             return;
         }
+        Slog.d(TAG, "onEnumerated, remaining: " + remaining);
         ((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9d19fdf..e3feb74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -82,6 +82,7 @@
 
     private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
         if (identifier == null) {
+            Slog.d(TAG, "Null identifier");
             return;
         }
         Slog.v(TAG, "handleEnumeratedTemplate: " + identifier.getBiometricId());
@@ -103,6 +104,7 @@
 
     private void doTemplateCleanup() {
         if (mEnrolledList == null) {
+            Slog.d(TAG, "Null enrolledList");
             return;
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
new file mode 100644
index 0000000..769c47a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
+import android.hardware.biometrics.face.Cell;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollFrame;
+
+/**
+ * Utilities for converting between hardware and framework-defined AIDL models.
+ */
+final class AidlConversionUtils {
+    // Prevent instantiation.
+    private AidlConversionUtils() {}
+
+    @NonNull
+    public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) {
+        return new FaceAuthenticationFrame(convert(frame.data));
+    }
+
+    @NonNull
+    public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
+        final AuthenticationFrame convertedFrame = new AuthenticationFrame();
+        convertedFrame.data = convert(frame.getData());
+        return convertedFrame;
+    }
+
+    @NonNull
+    public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
+        return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+    }
+
+    @NonNull
+    public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) {
+        final EnrollmentFrame convertedFrame = new EnrollmentFrame();
+        convertedFrame.cell = convert(frame.getCell());
+        convertedFrame.stage = (byte) frame.getStage();
+        convertedFrame.data = convert(frame.getData());
+        return convertedFrame;
+    }
+
+    @NonNull
+    public static FaceDataFrame convert(@NonNull BaseFrame frame) {
+        return new FaceDataFrame(
+                frame.acquiredInfo,
+                frame.vendorCode,
+                frame.pan,
+                frame.tilt,
+                frame.distance,
+                frame.isCancellable);
+    }
+
+    @NonNull
+    public static BaseFrame convert(@NonNull FaceDataFrame frame) {
+        final BaseFrame convertedFrame = new BaseFrame();
+        convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo();
+        convertedFrame.vendorCode = frame.getVendorCode();
+        convertedFrame.pan = frame.getPan();
+        convertedFrame.tilt = frame.getTilt();
+        convertedFrame.distance = frame.getDistance();
+        convertedFrame.isCancellable = frame.isCancellable();
+        return convertedFrame;
+    }
+
+    @Nullable
+    public static FaceEnrollCell convert(@Nullable Cell cell) {
+        return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z);
+    }
+
+    @Nullable
+    public static Cell convert(@Nullable FaceEnrollCell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        final Cell convertedCell = new Cell();
+        convertedCell.x = cell.getX();
+        convertedCell.y = cell.getY();
+        convertedCell.z = cell.getZ();
+        return convertedCell;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index d2673d2..897ebd7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -24,6 +24,8 @@
 import android.hardware.biometrics.face.AuthenticationFrame;
 import android.hardware.biometrics.face.BaseFrame;
 import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
 import android.util.Slog;
@@ -117,6 +119,16 @@
         public void onChallengeInterruptFinished(int sensorId) {
 
         }
+
+        @Override
+        public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+        }
+
+        @Override
+        public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+        }
     };
 
     BiometricTestSessionImpl(@NonNull Context context, int sensorId,
@@ -183,7 +195,7 @@
         mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
     }
 
-    // TODO(b/174619156): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
+    // TODO(b/178414967): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
     @Override
     public void notifyAcquired(int userId, int acquireInfo) {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
@@ -194,7 +206,7 @@
         AuthenticationFrame authenticationFrame = new AuthenticationFrame();
         authenticationFrame.data = data;
 
-        // TODO(b/174619156): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
+        // TODO(b/178414967): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
         // This will need to call the correct callback once the onAcquired callback is removed.
         mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFrame(
                 authenticationFrame);
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 a7bfc4b..8f55402 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
@@ -28,6 +28,7 @@
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticationFrame;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -177,22 +178,42 @@
         return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor;
     }
 
-    private boolean shouldSend(int acquireInfo, int vendorCode) {
-        if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
-            return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode);
-        } else {
-            return !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
-        }
+    private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+        return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+                ? !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode)
+                : !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
     }
 
     @Override
     public void onAcquired(int acquireInfo, int vendorCode) {
         mLastAcquire = acquireInfo;
-
-        final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
+        final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
         onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
     }
 
+    /**
+     * Called each time a new frame is received during face authentication.
+     *
+     * @param frame Information about the current frame.
+     */
+    public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
+        // Log acquisition but don't send it to the client yet, since that's handled below.
+        final int acquireInfo = frame.getData().getAcquiredInfo();
+        final int vendorCode = frame.getData().getVendorCode();
+        mLastAcquire = acquireInfo;
+        onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+        final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+        if (shouldSend && getListener() != null) {
+            try {
+                getListener().onAuthenticationFrame(frame);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to send authentication frame", e);
+                mCallback.onClientFinished(this, false /* success */);
+            }
+        }
+    }
+
     @Override public void onLockoutTimed(long durationMillis) {
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
         // Lockout metrics are logged as an error code.
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 afc7f64..898d81b 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
@@ -27,6 +27,7 @@
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.ISession;
 import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollFrame;
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.NativeHandle;
@@ -99,17 +100,40 @@
                 getTargetUserId()).size() >= mMaxTemplatesPerUser;
     }
 
+    private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+        return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+                ? !Utils.listContains(mEnrollIgnoreListVendor, vendorCode)
+                : !Utils.listContains(mEnrollIgnoreList, acquireInfo);
+    }
+
     @Override
     public void onAcquired(int acquireInfo, int vendorCode) {
-        final boolean shouldSend;
-        if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
-            shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode);
-        } else {
-            shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo);
-        }
+        final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
         onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
     }
 
+    /**
+     * Called each time a new frame is received during face enrollment.
+     *
+     * @param frame Information about the current frame.
+     */
+    public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
+        // Log acquisition but don't send it to the client yet, since that's handled below.
+        final int acquireInfo = frame.getData().getAcquiredInfo();
+        final int vendorCode = frame.getData().getVendorCode();
+        onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+        final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+        if (shouldSend && getListener() != null) {
+            try {
+                getListener().onEnrollmentFrame(frame);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to send enrollment frame", e);
+                mCallback.onClientFinished(this, false /* success */);
+            }
+        }
+    }
+
     @Override
     protected void startHalOperation() {
         final ArrayList<Byte> token = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index e685ee2..1b6b9d7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -643,8 +643,7 @@
                 final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
-                sensor.getScheduler().recordCrashState();
-                sensor.getScheduler().reset();
+                sensor.onBinderDied();
             }
         });
     }
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 f49601a..baeb3fd 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
@@ -33,7 +33,6 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Slog;
@@ -45,7 +44,6 @@
 import com.android.server.biometrics.SensorStateProto;
 import com.android.server.biometrics.UserStateProto;
 import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -64,7 +62,7 @@
 /**
  * Maintains the state of a single sensor within an instance of the {@link IFace} HAL.
  */
-public class Sensor implements IBinder.DeathRecipient {
+public class Sensor {
 
     private boolean mTestHalEnabled;
 
@@ -170,33 +168,39 @@
 
         @Override
         public void onAuthenticationFrame(AuthenticationFrame frame) {
-            // TODO(b/174619156): propagate the frame to an AuthenticationClient
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
-                if (!(client instanceof AcquisitionClient)) {
-                    Slog.e(mTag, "onAcquired for non-acquisition client: "
+                if (!(client instanceof FaceAuthenticationClient)) {
+                    Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
+                            + Utils.getClientName(client));
+                    return;
+
+                }
+                if (frame == null) {
+                    Slog.e(mTag, "Received null authentication frame for client: "
                             + Utils.getClientName(client));
                     return;
                 }
-
-                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
-                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+                ((FaceAuthenticationClient) client).onAuthenticationFrame(
+                        AidlConversionUtils.convert(frame));
             });
         }
 
         @Override
         public void onEnrollmentFrame(EnrollmentFrame frame) {
-            // TODO(b/174619156): propagate the frame to an EnrollmentClient
             mHandler.post(() -> {
                 final BaseClientMonitor client = mScheduler.getCurrentClient();
-                if (!(client instanceof AcquisitionClient)) {
-                    Slog.e(mTag, "onAcquired for non-acquisition client: "
+                if (!(client instanceof FaceEnrollClient)) {
+                    Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
                             + Utils.getClientName(client));
                     return;
                 }
-
-                final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
-                acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+                if (frame == null) {
+                    Slog.e(mTag, "Received null enrollment frame for client: "
+                            + Utils.getClientName(client));
+                    return;
+                }
+                ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
             });
         }
 
@@ -476,7 +480,6 @@
                 mTag, mScheduler, sensorId, userId, callback);
 
         final ISession newSession = daemon.createSession(sensorId, userId, resultController);
-        newSession.asBinder().linkToDeath(this, 0 /* flags */);
         mCurrentSession = new Session(mTag, newSession, userId, resultController);
     }
 
@@ -518,24 +521,21 @@
         proto.end(sensorToken);
     }
 
-    @Override
-    public void binderDied() {
-        Slog.e(mTag, "Binder died");
-        mHandler.post(() -> {
-            final BaseClientMonitor client = mScheduler.getCurrentClient();
-            if (client instanceof Interruptable) {
-                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
-                final Interruptable interruptable = (Interruptable) client;
-                interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
+    public void onBinderDied() {
+        final BaseClientMonitor client = mScheduler.getCurrentClient();
+        if (client instanceof Interruptable) {
+            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            final Interruptable interruptable = (Interruptable) client;
+            interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    BiometricsProtoEnums.MODALITY_FACE,
+                    BiometricsProtoEnums.ISSUE_HAL_DEATH);
+        }
 
-                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.MODALITY_FACE,
-                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
-                mCurrentSession = null;
-            }
-        });
+        mScheduler.recordCrashState();
+        mScheduler.reset();
+        mCurrentSession = null;
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 4142a52..d519d60 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.hardware.biometrics.ITestSession;
 import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
 import android.util.Slog;
@@ -106,6 +108,16 @@
         public void onChallengeInterruptFinished(int sensorId) {
 
         }
+
+        @Override
+        public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+        }
+
+        @Override
+        public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+        }
     };
 
     BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull Face10 face10,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 0265cb9..b0e42cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,10 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +36,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricService;
@@ -49,6 +54,7 @@
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
@@ -80,6 +86,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -190,7 +197,7 @@
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
                 final IFingerprintServiceReceiver receiver, final String opPackageName,
-                boolean shouldLogMetrics) {
+                @FingerprintManager.EnrollReason int enrollReason) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -200,7 +207,7 @@
             }
 
             provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
-                    receiver, opPackageName, shouldLogMetrics);
+                    receiver, opPackageName, enrollReason);
         }
 
         @Override // Binder call
@@ -219,8 +226,8 @@
         @SuppressWarnings("deprecation")
         @Override // Binder call
         public void authenticate(final IBinder token, final long operationId,
-                @FingerprintManager.SensorId final int sensorId, final int userId,
-                final IFingerprintServiceReceiver receiver, final String opPackageName) {
+                final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
+                final String opPackageName) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -236,7 +243,7 @@
             final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
 
             // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
-            final long identity = Binder.clearCallingIdentity();
+            long identity = Binder.clearCallingIdentity();
             try {
                 if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
                     // If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -266,9 +273,101 @@
                 return;
             }
 
-            provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
-                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
-                    restricted, statsClient, isKeyguard);
+            final FingerprintSensorPropertiesInternal sensorProps =
+                    provider.second.getSensorProperties(sensorId);
+            if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
+                    && sensorProps != null && sensorProps.isAnyUdfpsType()) {
+                identity = Binder.clearCallingIdentity();
+                try {
+                    authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } else {
+                provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+                        0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+                        restricted, statsClient, isKeyguard);
+            }
+        }
+
+        private void authenticateWithPrompt(
+                final long operationId,
+                @NonNull final FingerprintSensorPropertiesInternal props,
+                final int userId,
+                final IFingerprintServiceReceiver receiver) {
+
+            final Context context = getUiContext();
+            final Executor executor = context.getMainExecutor();
+
+            final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
+                    .setTitle(context.getString(R.string.biometric_dialog_default_title))
+                    .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle))
+                    .setNegativeButton(
+                            context.getString(R.string.cancel),
+                            executor,
+                            (dialog, which) -> {
+                                try {
+                                    receiver.onError(
+                                            FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */);
+                                } catch (RemoteException e) {
+                                    Slog.e(TAG, "Remote exception in negative button onClick()", e);
+                                }
+                            })
+                    .setSensorId(props.sensorId)
+                    .build();
+
+            final BiometricPrompt.AuthenticationCallback promptCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode, CharSequence errString) {
+                            try {
+                                if (FingerprintUtils.isKnownErrorCode(errorCode)) {
+                                    receiver.onError(errorCode, 0 /* vendorCode */);
+                                } else {
+                                    receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode);
+                                }
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationError()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                BiometricPrompt.AuthenticationResult result) {
+                            final Fingerprint fingerprint = new Fingerprint("", 0, 0L);
+                            final boolean isStrong = props.sensorStrength == STRENGTH_STRONG;
+                            try {
+                                receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            try {
+                                receiver.onAuthenticationFailed();
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationAcquired(int acquireInfo) {
+                            try {
+                                if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) {
+                                    receiver.onAcquired(acquireInfo, 0 /* vendorCode */);
+                                } else {
+                                    receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo);
+                                }
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e);
+                            }
+                        }
+                    };
+
+            biometricPrompt.authenticateUserForOperation(
+                    new CancellationSignal(), executor, promptCallback, userId, operationId);
         }
 
         @Override
@@ -374,6 +473,7 @@
         @Override // Binder call
         public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
                 final String opPackageName) {
+
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index dc6fd3a..d69151d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,18 @@
 
 package com.android.server.biometrics.sensors.fingerprint;
 
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
 import android.hardware.fingerprint.Fingerprint;
 import android.text.TextUtils;
 import android.util.SparseArray;
@@ -138,5 +148,51 @@
             return state;
         }
     }
+
+    /**
+     * Checks if the given error code corresponds to a known fingerprint error.
+     *
+     * @param errorCode The error code to be checked.
+     * @return Whether the error code corresponds to a known error.
+     */
+    public static boolean isKnownErrorCode(int errorCode) {
+        switch (errorCode) {
+            case FingerprintError.ERROR_HW_UNAVAILABLE:
+            case FingerprintError.ERROR_UNABLE_TO_PROCESS:
+            case FingerprintError.ERROR_TIMEOUT:
+            case FingerprintError.ERROR_NO_SPACE:
+            case FingerprintError.ERROR_CANCELED:
+            case FingerprintError.ERROR_UNABLE_TO_REMOVE:
+            case FingerprintError.ERROR_LOCKOUT:
+            case FingerprintError.ERROR_VENDOR:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if the given acquired code corresponds to a known fingerprint error.
+     *
+     * @param acquiredCode The acquired code to be checked.
+     * @return Whether the acquired code corresponds to a known error.
+     */
+    public static boolean isKnownAcquiredCode(int acquiredCode) {
+        switch (acquiredCode) {
+            case FINGERPRINT_ACQUIRED_GOOD:
+            case FINGERPRINT_ACQUIRED_PARTIAL:
+            case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+            case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+            case FINGERPRINT_ACQUIRED_TOO_SLOW:
+            case FINGERPRINT_ACQUIRED_TOO_FAST:
+            case FINGERPRINT_ACQUIRED_VENDOR:
+            case FINGERPRINT_ACQUIRED_START:
+                return true;
+
+            default:
+                return false;
+        }
+    }
 }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 303c080..f672ae5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -78,7 +78,7 @@
 
     void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
-            boolean shouldLogMetrics);
+            @FingerprintManager.EnrollReason int enrollReason);
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index d092e86..37f8e8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -65,6 +65,17 @@
         }
     }
 
+    public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+        switch (reason) {
+            case FingerprintManager.ENROLL_FIND_SENSOR:
+                return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR;
+            case FingerprintManager.ENROLL_ENROLL:
+                return IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+            default:
+                return IUdfpsOverlayController.REASON_UNKNOWN;
+        }
+    }
+
     public static void showUdfpsOverlay(int sensorId, int reason,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         if (udfpsOverlayController == null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index c2a30be..ea9c709 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.biometrics.ITestSession;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.os.Binder;
 import android.util.Slog;
@@ -131,7 +132,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
-                mContext.getOpPackageName(), true /* shouldLogMetrics */);
+                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
     }
 
     @Override
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 9611192..bcd1b8b 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
@@ -98,4 +98,9 @@
     public int getProtoEnum() {
         return BiometricsProto.CM_DETECT_INTERACTION;
     }
+
+    @Override
+    public boolean interruptsPrecedingClients() {
+        return true;
+    }
 }
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 08cc464..ae64c77 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
@@ -25,6 +25,7 @@
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -43,6 +44,7 @@
     private static final String TAG = "FingerprintEnrollClient";
 
     @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+    private final @FingerprintManager.EnrollReason int mEnrollReason;
     @Nullable private ICancellationSignal mCancellationSignal;
     private final int mMaxTemplatesPerUser;
 
@@ -52,13 +54,17 @@
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser,
-            boolean shouldLogMetrics) {
+            @FingerprintManager.EnrollReason int enrollReason) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 true /* shouldVibrate */);
         mUdfpsOverlayController = udfpsOvelayController;
         mMaxTemplatesPerUser = maxTemplatesPerUser;
-        setShouldLog(shouldLogMetrics);
+
+        mEnrollReason = enrollReason;
+        if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+            setShouldLog(false);
+        }
     }
 
     @Override
@@ -72,6 +78,7 @@
         }
     }
 
+
     @Override
     public void onAcquired(int acquiredInfo, int vendorCode) {
         super.onAcquired(acquiredInfo, vendorCode);
@@ -112,7 +119,8 @@
 
     @Override
     protected void startHalOperation() {
-        UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+        UdfpsHelper.showUdfpsOverlay(getSensorId(),
+                UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
                 mUdfpsOverlayController);
         try {
             mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f845024..0bd2f24 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -28,6 +28,7 @@
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -96,7 +97,8 @@
                         Slog.e(getTag(), "Task stack changed for client: " + client);
                         continue;
                     }
-                    if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                    if (Utils.isKeyguard(mContext, client.getOwnerString())
+                            || Utils.isSystem(mContext, client.getOwnerString())) {
                         continue; // Keyguard is always allowed
                     }
 
@@ -365,7 +367,7 @@
     @Override
     public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
             int userId, @NonNull IFingerprintServiceReceiver receiver,
-            @NonNull String opPackageName, boolean shouldLogMetrics) {
+            @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
         mHandler.post(() -> {
             final IFingerprint daemon = getHalInstance();
             if (daemon == null) {
@@ -387,7 +389,7 @@
                         mSensors.get(sensorId).getLazySession(), token,
                         new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
                         opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
-                        mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics);
+                        mUdfpsOverlayController, maxTemplatesPerUser, enrollReason);
                 scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
                     @Override
                     public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -695,8 +697,7 @@
                 final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
-                sensor.getScheduler().recordCrashState();
-                sensor.getScheduler().reset();
+                sensor.onBinderDied();
             }
         });
     }
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 f0e7e1c..7e4ee9e7 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
@@ -31,7 +31,6 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Slog;
@@ -65,7 +64,7 @@
  * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
  */
 @SuppressWarnings("deprecation")
-class Sensor implements IBinder.DeathRecipient {
+class Sensor {
 
     private boolean mTestHalEnabled;
 
@@ -461,7 +460,6 @@
                 mTag, mScheduler, sensorId, userId, callback);
 
         final ISession newSession = daemon.createSession(sensorId, userId, resultController);
-        newSession.asBinder().linkToDeath(this, 0 /* flags */);
         mCurrentSession = new Session(mTag, newSession, userId, resultController);
     }
 
@@ -503,24 +501,21 @@
         proto.end(sensorToken);
     }
 
-    @Override
-    public void binderDied() {
-        Slog.e(mTag, "Binder died");
-        mHandler.post(() -> {
-            final BaseClientMonitor client = mScheduler.getCurrentClient();
-            if (client instanceof Interruptable) {
-                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
-                final Interruptable interruptable = (Interruptable) client;
-                interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
+    public void onBinderDied() {
+        final BaseClientMonitor client = mScheduler.getCurrentClient();
+        if (client instanceof Interruptable) {
+            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            final Interruptable interruptable = (Interruptable) client;
+            interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                    BiometricsProtoEnums.ISSUE_HAL_DEATH);
+        }
 
-                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.MODALITY_FINGERPRINT,
-                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
-                mCurrentSession = null;
-            }
-        });
+        mScheduler.recordCrashState();
+        mScheduler.reset();
+        mCurrentSession = null;
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 6893e72..312ee0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.biometrics.ITestSession;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.os.Binder;
 import android.util.Slog;
@@ -132,7 +133,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
-                mContext.getOpPackageName(), true/* shouldLogMetrics */);
+                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 1135126..7a74c6a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -49,6 +50,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.SensorServiceStateProto;
 import com.android.server.biometrics.SensorStateProto;
@@ -113,7 +115,7 @@
     @NonNull private final HalResultController mHalResultController;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
     private int mCurrentUserId = UserHandle.USER_NULL;
-    private boolean mIsUdfps = false;
+    private final boolean mIsUdfps;
     private final int mSensorId;
 
     private final class BiometricTaskStackListener extends TaskStackListener {
@@ -125,7 +127,8 @@
                     Slog.e(TAG, "Task stack changed for client: " + client);
                     return;
                 }
-                if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                if (Utils.isKeyguard(mContext, client.getOwnerString())
+                        || Utils.isSystem(mContext, client.getOwnerString())) {
                     return; // Keyguard is always allowed
                 }
 
@@ -335,23 +338,14 @@
             Slog.e(TAG, "Unable to register user switch observer");
         }
 
-        final IBiometricsFingerprint daemon = getDaemon();
-        mIsUdfps = false;
-        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
-                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
-                        daemon);
-        if (extension != null) {
-            try {
-                mIsUdfps = extension.isUdfps(sensorId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception while quering udfps", e);
-                mIsUdfps = false;
-            }
-        }
+        // TODO(b/179175438): Remove this code block after transition to AIDL.
+        // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS.
+        mIsUdfps = !ArrayUtils.isEmpty(
+                mContext.getResources().getIntArray(R.array.config_udfps_sensor_props));
 
         final @FingerprintSensorProperties.SensorType int sensorType =
                 mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
-                         : FingerprintSensorProperties.TYPE_REAR;
+                        : FingerprintSensorProperties.TYPE_REAR;
         // resetLockout is controlled by the framework, so hardwareAuthToken is not required
         final boolean resetLockoutRequiresHardwareAuthToken = false;
         final int maxEnrollmentsPerUser = mContext.getResources()
@@ -415,7 +409,7 @@
         Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
                 + mScheduler.getCurrentClient());
         try {
-            mDaemon = IBiometricsFingerprint.getService(true /* retry */);
+            mDaemon = IBiometricsFingerprint.getService();
         } catch (java.util.NoSuchElementException e) {
             // Service doesn't exist or cannot be opened.
             Slog.w(TAG, "NoSuchElementException", e);
@@ -554,7 +548,7 @@
     public void scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
-            boolean shouldLogMetrics) {
+            @FingerprintManager.EnrollReason int enrollReason) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -562,7 +556,7 @@
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
                     hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
                     ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
-                    shouldLogMetrics);
+                    enrollReason);
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 7989dca..8acb284 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -130,4 +130,9 @@
     public int getProtoEnum() {
         return BiometricsProto.CM_DETECT_INTERACTION;
     }
+
+    @Override
+    public boolean interruptsPrecedingClients() {
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index d927aa7..33db64c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -24,6 +24,7 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -46,6 +47,7 @@
     private static final String TAG = "FingerprintEnrollClient";
 
     @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+    private final @FingerprintManager.EnrollReason int mEnrollReason;
 
     FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -53,12 +55,16 @@
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
-            boolean shouldLogMetrics) {
+            @FingerprintManager.EnrollReason int enrollReason) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 true /* shouldVibrate */);
         mUdfpsOverlayController = udfpsOverlayController;
-        setShouldLog(shouldLogMetrics);
+
+        mEnrollReason = enrollReason;
+        if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+            setShouldLog(false);
+        }
     }
 
     @Override
@@ -76,7 +82,8 @@
 
     @Override
     protected void startHalOperation() {
-        UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+        UdfpsHelper.showUdfpsOverlay(getSensorId(),
+                UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
                 mUdfpsOverlayController);
         try {
             // GroupId was never used. In fact, groupId is always the same as userId.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6b2a1c9..51ba5f7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -211,9 +211,9 @@
     }
 
     @Override
-    public void clearOverrideForTest(long changeId, String packageName) {
+    public boolean clearOverrideForTest(long changeId, String packageName) {
         checkCompatChangeOverridePermission();
-        mCompatConfig.removeOverride(changeId, packageName);
+        return mCompatConfig.removeOverride(changeId, packageName);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb08..43d9ade 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.IDnsResolver;
+import android.net.InetAddresses;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@
             for (String ipAddress : ipAddresses) {
                 try {
                     latestDnses.add(new Pair(hostname,
-                            InetAddress.parseNumericAddress(ipAddress)));
+                            InetAddresses.parseNumericAddress(ipAddress)));
                 } catch (IllegalArgumentException e) {}
             }
             // Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b..46c49e7 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.net.BaseNetworkObserver;
 
-import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.util.Objects;
 
@@ -433,7 +433,7 @@
         // clat IPv4 address itself (for those apps, it doesn't matter what
         // the IP of the gateway is, only that there is one).
         RouteInfo ipv4Default = new RouteInfo(
-                new LinkAddress(Inet4Address.ANY, 0),
+                new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
                 clatAddress.getAddress(), mIface);
         stacked.addRoute(ipv4Default);
         stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a9a705f..c05e253 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -122,6 +122,13 @@
 //
 // When ConnectivityService disconnects a network:
 // -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
 // If a network has no chance of satisfying any requests (even if it were to become validated
 // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
 //
@@ -271,7 +278,8 @@
 
     // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
     // a request is moved to a network with a better score, regardless of whether the network is or
-    // was lingering or not.
+    // was lingering or not. An inactivity timer is also added when a network connects
+    // without immediately satisfying any requests.
     // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
     // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
     private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
@@ -709,8 +717,9 @@
                 mNumBackgroundNetworkRequests += delta;
                 break;
 
-            case TRACK_DEFAULT:
             case LISTEN:
+            case TRACK_DEFAULT:
+            case TRACK_SYSTEM_DEFAULT:
                 break;
 
             case NONE:
@@ -896,7 +905,12 @@
 
     /**
      * Sets the specified requestId to linger on this network for the specified time. Called by
-     * ConnectivityService when the request is moved to another network with a higher score.
+     * ConnectivityService when the request is moved to another network with a higher score, or
+     * when a network is newly created.
+     *
+     * @param requestId The requestId of the request that no longer need to be served by this
+     *                  network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+     *                  {@code LingerTimer} for a newly created network.
      */
     public void lingerRequest(int requestId, long now, long duration) {
         if (mInactivityTimerForRequest.get(requestId) != null) {
@@ -969,10 +983,23 @@
         mInactive = false;
     }
 
-    public boolean isLingering() {
+    public boolean isInactive() {
         return mInactive;
     }
 
+    public boolean isLingering() {
+        return mInactive && !isNascent();
+    }
+
+    /**
+     * Return whether the network is just connected and about to be torn down because of not
+     * satisfying any request.
+     */
+    public boolean isNascent() {
+        return mInactive && mInactivityTimers.size() == 1
+                && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+    }
+
     public void clearInactivityState() {
         if (mInactivityMessage != null) {
             mInactivityMessage.cancel();
@@ -1022,7 +1049,7 @@
                 + "network{" + network + "}  handle{" + network.getNetworkHandle() + "}  ni{"
                 + networkInfo.toShortString() + "} "
                 + "  Score{" + getCurrentScore() + "} "
-                + (isLingering() ? " lingering" : "")
+                + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
                 + (everValidated ? " everValidated" : "")
                 + (lastValidated ? " lastValidated" : "")
                 + (partialConnectivity ? " partialConnectivity" : "")
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f..8d21f6f 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -265,7 +265,10 @@
         for (Entry<Integer, Boolean> app : apps.entrySet()) {
             List<Integer> list = app.getValue() ? system : network;
             for (int user : users) {
-                list.add(UserHandle.getUid(user, app.getKey()));
+                final UserHandle handle = UserHandle.of(user);
+                if (handle == null) continue;
+
+                list.add(UserHandle.getUid(handle, app.getKey()));
             }
         }
         try {
@@ -550,7 +553,10 @@
         for (UidRange range : ranges) {
             for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
                 for (int appId : appIds) {
-                    final int uid = UserHandle.getUid(userId, appId);
+                    final UserHandle handle = UserHandle.of(userId);
+                    if (handle == null) continue;
+
+                    final int uid = UserHandle.getUid(handle, appId);
                     if (range.contains(uid)) {
                         result.add(uid);
                     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5956fe1..33c19b1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@
 import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
 import android.net.IpSecManager.IpSecTunnelInterface;
@@ -73,6 +74,7 @@
 import android.net.UnderlyingNetworkInfo;
 import android.net.VpnManager;
 import android.net.VpnService;
+import android.net.VpnTransportInfo;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.ChildSessionConfiguration;
 import android.net.ipsec.ike.ChildSessionParams;
@@ -111,6 +113,7 @@
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +206,7 @@
     protected final NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
     private final Ikev2SessionCreator mIkev2SessionCreator;
+    private final UserManager mUserManager;
 
     /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -331,7 +335,7 @@
         public InetAddress resolve(final String endpoint)
                 throws ExecutionException, InterruptedException {
             try {
-                return InetAddress.parseNumericAddress(endpoint);
+                return InetAddresses.parseNumericAddress(endpoint);
             } catch (IllegalArgumentException e) {
                 // Endpoint is not numeric : fall through and resolve
             }
@@ -409,6 +413,7 @@
         mLooper = looper;
         mSystemServices = systemServices;
         mIkev2SessionCreator = ikev2SessionCreator;
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserId);
@@ -431,6 +436,7 @@
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+        mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
 
         loadAlwaysOnPackage(keyStore);
     }
@@ -925,6 +931,7 @@
                 jniReset(mInterface);
                 mInterface = null;
                 mNetworkCapabilities.setUids(null);
+                mNetworkCapabilities.setTransportInfo(null);
             }
 
             // Revoke the connection or stop the VpnRunner.
@@ -995,6 +1002,8 @@
                 case VpnManager.TYPE_VPN_SERVICE:
                     toChange = new String[] {AppOpsManager.OPSTR_ACTIVATE_VPN};
                     break;
+                case VpnManager.TYPE_VPN_LEGACY:
+                    return false;
                 default:
                     Log.wtf(TAG, "Unrecognized VPN type while granting authorization");
                     return false;
@@ -1025,6 +1034,8 @@
                 return isVpnServicePreConsented(context, packageName);
             case VpnManager.TYPE_VPN_PLATFORM:
                 return isVpnProfilePreConsented(context, packageName);
+            case VpnManager.TYPE_VPN_LEGACY:
+                return VpnConfig.LEGACY_VPN.equals(packageName);
             default:
                 return false;
         }
@@ -1123,7 +1134,7 @@
 
         if (mConfig.dnsServers != null) {
             for (String dnsServer : mConfig.dnsServers) {
-                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+                InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
                 lp.addDnsServer(address);
                 allowIPv4 |= address instanceof Inet4Address;
                 allowIPv6 |= address instanceof Inet6Address;
@@ -1133,10 +1144,12 @@
         lp.setHttpProxy(mConfig.proxyInfo);
 
         if (!allowIPv4) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
         }
         if (!allowIPv6) {
-            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+            lp.addRoute(new RouteInfo(new IpPrefix(
+                    NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
         }
 
         // Concatenate search domains into a string.
@@ -1205,6 +1218,8 @@
         mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                 mConfig.allowedApplications, mConfig.disallowedApplications));
 
+        mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+
         // Only apps targeting Q and above can explicitly declare themselves as metered.
         // These VPNs are assumed metered unless they state otherwise.
         if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) {
@@ -1435,7 +1450,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getAliveUsers();
+                users = mUserManager.getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1519,7 +1534,7 @@
      */
     public void onUserAdded(int userId) {
         // If the user is restricted tie them to the parent user's VPN
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1547,7 +1562,7 @@
      */
     public void onUserRemoved(int userId) {
         // clean up if restricted
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1730,6 +1745,7 @@
     private void cleanupVpnStateLocked() {
         mStatusIntent = null;
         mNetworkCapabilities.setUids(null);
+        mNetworkCapabilities.setTransportInfo(null);
         mConfig = null;
         mInterface = null;
 
@@ -1840,22 +1856,18 @@
     }
 
     /**
-     * Gets the currently running App-based VPN type
+     * Gets the currently running VPN type
      *
-     * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an
-     *     app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
+     * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running a
+     *     VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
      *     Settings-based, the Platform VPNs can be initiated by both apps and Settings.
      */
-    public synchronized int getActiveAppVpnType() {
-        if (VpnConfig.LEGACY_VPN.equals(mPackage)) {
-            return VpnManager.TYPE_VPN_NONE;
-        }
-
-        if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) {
-            return VpnManager.TYPE_VPN_PLATFORM;
-        } else {
-            return VpnManager.TYPE_VPN_SERVICE;
-        }
+    public synchronized int getActiveVpnType() {
+        if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE;
+        if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE;
+        return mVpnRunner instanceof IkeV2VpnRunner
+                ? VpnManager.TYPE_VPN_PLATFORM
+                : VpnManager.TYPE_VPN_LEGACY;
     }
 
     private void updateAlwaysOnNotification(DetailedState networkState) {
@@ -1972,8 +1984,7 @@
 
     private void enforceNotRestrictedUser() {
         Binder.withCleanCallingIdentity(() -> {
-            final UserManager mgr = UserManager.get(mContext);
-            final UserInfo user = mgr.getUserInfo(mUserId);
+            final UserInfo user = mUserManager.getUserInfo(mUserId);
 
             if (user.isRestricted()) {
                 throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2008,9 +2019,8 @@
      */
     public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
             @Nullable Network underlying, @NonNull LinkProperties egress) {
-        UserManager mgr = UserManager.get(mContext);
-        UserInfo user = mgr.getUserInfo(mUserId);
-        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+        UserInfo user = mUserManager.getUserInfo(mUserId);
+        if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
                     new UserHandle(mUserId))) {
             throw new SecurityException("Restricted users cannot establish VPNs");
         }
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 802472f..e496d77 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -16,8 +16,6 @@
 
 package com.android.server.devicestate;
 
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 
@@ -37,16 +35,16 @@
  */
 public final class DeviceState {
     /** Unique identifier for the device state. */
-    @IntRange(from = INVALID_DEVICE_STATE)
+    @IntRange(from = 0)
     private final int mIdentifier;
 
     /** String description of the device state. */
     @NonNull
     private final String mName;
 
-    public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+    public DeviceState(@IntRange(from = 0) int identifier,
             @NonNull String name) {
-        if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
+        if (identifier < 0) {
             throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
         }
         mIdentifier = identifier;
@@ -54,7 +52,7 @@
     }
 
     /** Returns the unique identifier for the device state. */
-    @IntRange(from = INVALID_DEVICE_STATE)
+    @IntRange(from = 0)
     public int getIdentifier() {
         return mIdentifier;
     }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 375ec3a..984a176 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,13 +17,13 @@
 package com.android.server.devicestate;
 
 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 import android.os.Binder;
@@ -31,6 +31,8 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -62,8 +64,12 @@
  * the {@link DeviceStateProvider} to modify the current device state and communicating with the
  * {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
  * </p>
+ * The service also provides the {@link DeviceStateManager} API allowing clients to listen for
+ * changes in device state and submit requests to override the device state provided by the
+ * {@link DeviceStateProvider}.
  *
  * @see DeviceStatePolicy
+ * @see DeviceStateManager
  */
 public final class DeviceStateManagerService extends SystemService {
     private static final String TAG = "DeviceStateManagerService";
@@ -79,11 +85,11 @@
     @GuardedBy("mLock")
     private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
 
-    // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
+    // The current committed device state. The default of UNSET will be replaced by
     // the current state after the initial callback from the DeviceStateProvider.
     @GuardedBy("mLock")
     @NonNull
-    private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
+    private DeviceState mCommittedState = new DeviceState(0, "UNSET");
     // The device state that is currently awaiting callback from the policy to be committed.
     @GuardedBy("mLock")
     @NonNull
@@ -91,19 +97,23 @@
     // Whether or not the policy is currently waiting to be notified of the current pending state.
     @GuardedBy("mLock")
     private boolean mIsPolicyWaitingForState = false;
-    // The device state that is currently requested and is next to be configured and committed.
-    // Can be overwritten by an override state value if requested.
-    @GuardedBy("mLock")
-    @NonNull
-    private Optional<DeviceState> mRequestedState = Optional.empty();
-    // The most recently requested override state, or empty if no override is requested.
-    @GuardedBy("mLock")
-    @NonNull
-    private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
 
-    // List of registered callbacks indexed by process id.
+    // The device state that is set by the device state provider.
     @GuardedBy("mLock")
-    private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
+    @NonNull
+    private Optional<DeviceState> mBaseState = Optional.empty();
+
+    // List of processes registered to receive notifications about changes to device state and
+    // request status indexed by process id.
+    @GuardedBy("mLock")
+    private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
+    // List of override requests with the highest precedence request at the end.
+    @GuardedBy("mLock")
+    private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
+    // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
+    // of a change in status.
+    @GuardedBy("mLock")
+    private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
 
     public DeviceStateManagerService(@NonNull Context context) {
         this(context, new DeviceStatePolicyImpl(context));
@@ -148,55 +158,32 @@
     }
 
     /**
-     * Returns the requested state. The service will configure the device to match the requested
-     * state when possible.
+     * Returns the base state. The service will configure the device to match the base state when
+     * there is no active request to override the base state.
+     *
+     * @see #getOverrideState()
      */
     @NonNull
-    Optional<DeviceState> getRequestedState() {
+    Optional<DeviceState> getBaseState() {
         synchronized (mLock) {
-            return mRequestedState;
+            return mBaseState;
         }
     }
 
     /**
-     * Overrides the current device state with the provided state.
-     *
-     * @return {@code true} if the override state is valid and supported, {@code false} otherwise.
-     */
-    boolean setOverrideState(int overrideState) {
-        if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
-        }
-
-        synchronized (mLock) {
-            if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
-                return false;
-            }
-
-            mRequestedOverrideState = getStateLocked(overrideState);
-            updatePendingStateLocked();
-        }
-
-        notifyPolicyIfNeeded();
-        return true;
-    }
-
-    /**
-     * Clears an override state set with {@link #setOverrideState(int)}.
-     */
-    void clearOverrideState() {
-        setOverrideState(INVALID_DEVICE_STATE);
-    }
-
-    /**
-     * Returns the current requested override state, or {@link Optional#empty()} if no override
-     * state is requested.
+     * Returns the current override state, or {@link Optional#empty()} if no override state is
+     * requested. If an override states is present, the returned state will take precedence over
+     * the base state returned from {@link #getBaseState()}.
      */
     @NonNull
     Optional<DeviceState> getOverrideState() {
         synchronized (mLock) {
-            return mRequestedOverrideState;
+            if (mRequestRecords.isEmpty()) {
+                return Optional.empty();
+            }
+
+            OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
+            return Optional.of(topRequest.mRequestedState);
         }
     }
 
@@ -211,6 +198,17 @@
         }
     }
 
+    /** Returns the list of currently supported device state identifiers. */
+    private int[] getSupportedStateIdentifiers() {
+        synchronized (mLock) {
+            int[] supportedStates = new int[mDeviceStates.size()];
+            for (int i = 0; i < supportedStates.length; i++) {
+                supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
+            }
+            return supportedStates;
+        }
+    }
+
     @VisibleForTesting
     IDeviceStateManager getBinderService() {
         return mBinderService;
@@ -224,22 +222,26 @@
                 mDeviceStates.put(state.getIdentifier(), state);
             }
 
-            if (mRequestedState.isPresent()
-                    && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
-                // The current requested state is no longer valid. We'll clear it here, though
+            if (mBaseState.isPresent()
+                    && !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
+                // The current base state is no longer valid. We'll clear it here, though
                 // we won't actually update the current state until a callback comes from the
                 // provider with the most recent state.
-                mRequestedState = Optional.empty();
+                mBaseState = Optional.empty();
             }
-            if (mRequestedOverrideState.isPresent()
-                    && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
-                // The current override state is no longer valid. We'll clear it here and update
-                // the committed state if necessary.
-                mRequestedOverrideState = Optional.empty();
+
+            final int requestSize = mRequestRecords.size();
+            for (int i = 0; i < requestSize; i++) {
+                OverrideRequestRecord request = mRequestRecords.get(i);
+                if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
+                    request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+                }
             }
+
             updatePendingStateLocked();
         }
 
+        notifyRequestsOfStatusChangeIfNeeded();
         notifyPolicyIfNeeded();
     }
 
@@ -261,20 +263,37 @@
     }
 
     /**
-     * Requests that the system enter the provided {@code state}. The request may not be honored
-     * under certain conditions, for example if the provided state is not supported.
+     * Requests to set the base state. The request may not be honored under certain conditions, for
+     * example if the provided state is not supported.
      *
      * @see #isSupportedStateLocked(int)
      */
-    private void requestState(int identifier) {
+    private void setBaseState(int identifier) {
         synchronized (mLock) {
-            final Optional<DeviceState> requestedState = getStateLocked(identifier);
-            if (requestedState.isPresent()) {
-                mRequestedState = requestedState;
+            if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
+                // Base state hasn't changed. Nothing to do.
+                return;
             }
+
+            final Optional<DeviceState> baseState = getStateLocked(identifier);
+            if (!baseState.isPresent()) {
+                throw new IllegalArgumentException("Base state is not supported");
+            }
+
+            mBaseState = baseState;
+
+            final int requestSize = mRequestRecords.size();
+            for (int i = 0; i < requestSize; i++) {
+                OverrideRequestRecord request = mRequestRecords.get(i);
+                if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
+                    request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+                }
+            }
+
             updatePendingStateLocked();
         }
 
+        notifyRequestsOfStatusChangeIfNeeded();
         notifyPolicyIfNeeded();
     }
 
@@ -290,10 +309,10 @@
         }
 
         final DeviceState stateToConfigure;
-        if (mRequestedOverrideState.isPresent()) {
-            stateToConfigure = mRequestedOverrideState.get();
+        if (!mRequestRecords.isEmpty()) {
+            stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
         } else {
-            stateToConfigure = mRequestedState.orElse(null);
+            stateToConfigure = mBaseState.orElse(null);
         }
 
         if (stateToConfigure == null) {
@@ -360,6 +379,13 @@
             }
             mCommittedState = mPendingState.get();
             newState = mCommittedState.getIdentifier();
+
+            if (!mRequestRecords.isEmpty()) {
+                final OverrideRequestRecord topRequest =
+                        mRequestRecords.get(mRequestRecords.size() - 1);
+                topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+            }
+
             mPendingState = Optional.empty();
             updatePendingStateLocked();
         }
@@ -367,6 +393,9 @@
         // Notify callbacks of a change.
         notifyDeviceStateChanged(newState);
 
+        // Notify the top request that it's active.
+        notifyRequestsOfStatusChangeIfNeeded();
+
         // Try to configure the next state if needed.
         notifyPolicyIfNeeded();
     }
@@ -377,43 +406,69 @@
                     "Attempting to notify callbacks with service lock held.");
         }
 
-        // Grab the lock and copy the callbacks.
-        ArrayList<CallbackRecord> callbacks;
+        // Grab the lock and copy the process records.
+        ArrayList<ProcessRecord> registeredProcesses;
         synchronized (mLock) {
-            if (mCallbacks.size() == 0) {
+            if (mProcessRecords.size() == 0) {
                 return;
             }
 
-            callbacks = new ArrayList<>();
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                callbacks.add(mCallbacks.valueAt(i));
+            registeredProcesses = new ArrayList<>();
+            for (int i = 0; i < mProcessRecords.size(); i++) {
+                registeredProcesses.add(mProcessRecords.valueAt(i));
             }
         }
 
         // After releasing the lock, send the notifications out.
-        for (int i = 0; i < callbacks.size(); i++) {
-            callbacks.get(i).notifyDeviceStateAsync(deviceState);
+        for (int i = 0; i < registeredProcesses.size(); i++) {
+            registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
         }
     }
 
-    private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
+    /**
+     * Notifies all dirty requests (requests that have a change in status, but have not yet been
+     * notified) that their status has changed.
+     */
+    private void notifyRequestsOfStatusChangeIfNeeded() {
+        if (Thread.holdsLock(mLock)) {
+            throw new IllegalStateException(
+                    "Attempting to notify requests with service lock held.");
+        }
+
+        ArraySet<OverrideRequestRecord> dirtyRequests;
+        synchronized (mLock) {
+            if (mRequestsPendingStatusChange.isEmpty()) {
+                return;
+            }
+
+            dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
+            mRequestsPendingStatusChange.clear();
+        }
+
+        // After releasing the lock, send the notifications out.
+        for (int i = 0; i < dirtyRequests.size(); i++) {
+            dirtyRequests.valueAt(i).notifyStatusIfNeeded();
+        }
+    }
+
+    private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
         int currentState;
-        CallbackRecord record;
+        ProcessRecord record;
         // Grab the lock to register the callback and get the current state.
         synchronized (mLock) {
-            if (mCallbacks.contains(callingPid)) {
+            if (mProcessRecords.contains(pid)) {
                 throw new SecurityException("The calling process has already registered an"
                         + " IDeviceStateManagerCallback.");
             }
 
-            record = new CallbackRecord(callback, callingPid);
+            record = new ProcessRecord(callback, pid);
             try {
                 callback.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
                 throw new RuntimeException(ex);
             }
 
-            mCallbacks.put(callingPid, record);
+            mProcessRecords.put(pid, record);
             currentState = mCommittedState.getIdentifier();
         }
 
@@ -421,10 +476,86 @@
         record.notifyDeviceStateAsync(currentState);
     }
 
-    private void unregisterCallbackInternal(CallbackRecord record) {
+    private void handleProcessDied(ProcessRecord processRecord) {
         synchronized (mLock) {
-            mCallbacks.remove(record.mPid);
+            // Cancel all requests from this process.
+            final int requestCount = processRecord.mRequestRecords.size();
+            for (int i = 0; i < requestCount; i++) {
+                final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
+                // Cancel the request but don't mark it as dirty since there's no need to send
+                // notifications if the process has died.
+                request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
+                        false /* markDirty */);
+            }
+
+            mProcessRecords.remove(processRecord.mPid);
+
+            updatePendingStateLocked();
         }
+
+        notifyPolicyIfNeeded();
+    }
+
+    private void requestStateInternal(int state, int flags, int callingPid,
+            @NonNull IBinder token) {
+        synchronized (mLock) {
+            final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+            if (processRecord == null) {
+                throw new IllegalStateException("Process " + callingPid
+                        + " has no registered callback.");
+            }
+
+            if (processRecord.mRequestRecords.get(token) != null) {
+                throw new IllegalStateException("Request has already been made for the supplied"
+                        + " token: " + token);
+            }
+
+            final Optional<DeviceState> deviceState = getStateLocked(state);
+            if (!deviceState.isPresent()) {
+                throw new IllegalArgumentException("Requested state: " + state
+                        + " is not supported.");
+            }
+
+            OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
+                    ? null : mRequestRecords.get(mRequestRecords.size() - 1);
+            if (topRecord != null) {
+                topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
+            }
+
+            final OverrideRequestRecord request =
+                    new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
+            mRequestRecords.add(request);
+            processRecord.mRequestRecords.put(request.mToken, request);
+            // We don't set the status of the new request to ACTIVE here as it will be set in
+            // commitPendingState().
+
+            updatePendingStateLocked();
+        }
+
+        notifyRequestsOfStatusChangeIfNeeded();
+        notifyPolicyIfNeeded();
+    }
+
+    private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
+        synchronized (mLock) {
+            final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+            if (processRecord == null) {
+                throw new IllegalStateException("Process " + callingPid
+                        + " has no registered callback.");
+            }
+
+            OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
+            if (request == null) {
+                throw new IllegalStateException("No known request for the given token");
+            }
+
+            request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+
+            updatePendingStateLocked();
+        }
+
+        notifyRequestsOfStatusChangeIfNeeded();
+        notifyPolicyIfNeeded();
     }
 
     private void dumpInternal(PrintWriter pw) {
@@ -433,15 +564,26 @@
         synchronized (mLock) {
             pw.println("  mCommittedState=" + mCommittedState);
             pw.println("  mPendingState=" + mPendingState);
-            pw.println("  mRequestedState=" + mRequestedState);
-            pw.println("  mRequestedOverrideState=" + mRequestedOverrideState);
+            pw.println("  mBaseState=" + mBaseState);
+            pw.println("  mOverrideState=" + getOverrideState());
 
-            final int callbackCount = mCallbacks.size();
+            final int processCount = mProcessRecords.size();
             pw.println();
-            pw.println("Callbacks: size=" + callbackCount);
-            for (int i = 0; i < callbackCount; i++) {
-                CallbackRecord callback = mCallbacks.valueAt(i);
-                pw.println("  " + i + ": mPid=" + callback.mPid);
+            pw.println("Registered processes: size=" + processCount);
+            for (int i = 0; i < processCount; i++) {
+                ProcessRecord processRecord = mProcessRecords.valueAt(i);
+                pw.println("  " + i + ": mPid=" + processRecord.mPid);
+            }
+
+            final int requestCount = mRequestRecords.size();
+            pw.println();
+            pw.println("Override requests: size=" + requestCount);
+            for (int i = 0; i < requestCount; i++) {
+                OverrideRequestRecord requestRecord = mRequestRecords.get(i);
+                pw.println("  " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
+                        + ", mRequestedState=" + requestRecord.mRequestedState
+                        + ", mFlags=" + requestRecord.mFlags
+                        + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
             }
         }
     }
@@ -452,12 +594,6 @@
             if (newDeviceStates.length == 0) {
                 throw new IllegalArgumentException("Supported device states must not be empty");
             }
-            for (int i = 0; i < newDeviceStates.length; i++) {
-                if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
-                    throw new IllegalArgumentException(
-                            "Supported device states includes INVALID_DEVICE_STATE identifier");
-                }
-            }
             updateSupportedStates(newDeviceStates);
         }
 
@@ -467,22 +603,24 @@
                 throw new IllegalArgumentException("Invalid identifier: " + identifier);
             }
 
-            requestState(identifier);
+            setBaseState(identifier);
         }
     }
 
-    private final class CallbackRecord implements IBinder.DeathRecipient {
+    private final class ProcessRecord implements IBinder.DeathRecipient {
         private final IDeviceStateManagerCallback mCallback;
         private final int mPid;
 
-        CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
+        private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
+
+        ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
             mCallback = callback;
             mPid = pid;
         }
 
         @Override
         public void binderDied() {
-            unregisterCallbackInternal(this);
+            handleProcessDied(this);
         }
 
         public void notifyDeviceStateAsync(int devicestate) {
@@ -493,6 +631,119 @@
                         ex);
             }
         }
+
+        public void notifyRequestActiveAsync(OverrideRequestRecord request) {
+            try {
+                mCallback.onRequestActive(request.mToken);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                        ex);
+            }
+        }
+
+        public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
+            try {
+                mCallback.onRequestSuspended(request.mToken);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                        ex);
+            }
+        }
+
+        public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
+            try {
+                mCallback.onRequestCanceled(request.mToken);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                        ex);
+            }
+        }
+    }
+
+    /** A record describing a request to override the state of the device. */
+    private final class OverrideRequestRecord {
+        public static final int STATUS_UNKNOWN = 0;
+        public static final int STATUS_ACTIVE = 1;
+        public static final int STATUS_SUSPENDED = 2;
+        public static final int STATUS_CANCELED = 3;
+
+        @Nullable
+        public String statusToString(int status) {
+            switch (status) {
+                case STATUS_ACTIVE:
+                    return "ACTIVE";
+                case STATUS_SUSPENDED:
+                    return "SUSPENDED";
+                case STATUS_CANCELED:
+                    return "CANCELED";
+                case STATUS_UNKNOWN:
+                    return "UNKNOWN";
+                default:
+                    return null;
+            }
+        }
+
+        private final ProcessRecord mProcessRecord;
+        @NonNull
+        private final IBinder mToken;
+        @NonNull
+        private final DeviceState mRequestedState;
+        private final int mFlags;
+
+        private int mStatus = STATUS_UNKNOWN;
+        private int mLastNotifiedStatus = STATUS_UNKNOWN;
+
+        OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
+                @NonNull DeviceState requestedState, int flags) {
+            mProcessRecord = processRecord;
+            mToken = token;
+            mRequestedState = requestedState;
+            mFlags = flags;
+        }
+
+        public void setStatusLocked(int status) {
+            setStatusLocked(status, true /* markDirty */);
+        }
+
+        public void setStatusLocked(int status, boolean markDirty) {
+            if (mStatus != status) {
+                if (mStatus == STATUS_CANCELED) {
+                    throw new IllegalStateException(
+                            "Can not alter the status of a request after set to CANCELED.");
+                }
+
+                mStatus = status;
+
+                if (mStatus == STATUS_CANCELED) {
+                    mRequestRecords.remove(this);
+                    mProcessRecord.mRequestRecords.remove(mToken);
+                }
+
+                if (markDirty) {
+                    mRequestsPendingStatusChange.add(this);
+                }
+            }
+        }
+
+        public void notifyStatusIfNeeded() {
+            int stateToReport;
+            synchronized (mLock) {
+                if (mLastNotifiedStatus == mStatus) {
+                    return;
+                }
+
+                stateToReport = mStatus;
+                mLastNotifiedStatus = mStatus;
+            }
+
+            if (stateToReport == STATUS_ACTIVE) {
+                mProcessRecord.notifyRequestActiveAsync(this);
+            } else if (stateToReport == STATUS_SUSPENDED) {
+                mProcessRecord.notifyRequestSuspendedAsync(this);
+            } else if (stateToReport == STATUS_CANCELED) {
+                mProcessRecord.notifyRequestCanceledAsync(this);
+            }
+        }
     }
 
     /** Implementation of {@link IDeviceStateManager} published as a binder service. */
@@ -506,13 +757,59 @@
             final int callingPid = Binder.getCallingPid();
             final long token = Binder.clearCallingIdentity();
             try {
-                registerCallbackInternal(callback, callingPid);
+                registerProcess(callingPid, callback);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
+        public int[] getSupportedDeviceStates() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getSupportedStateIdentifiers();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public void requestState(IBinder token, int state, int flags) {
+            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+                    "Permission required to request device state.");
+
+            if (token == null) {
+                throw new IllegalArgumentException("Request token must not be null.");
+            }
+
+            final int callingPid = Binder.getCallingPid();
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                requestStateInternal(state, flags, callingPid, token);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override // Binder call
+        public void cancelRequest(IBinder token) {
+            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+                    "Permission required to clear requested device state.");
+
+            if (token == null) {
+                throw new IllegalArgumentException("Request token must not be null.");
+            }
+
+            final int callingPid = Binder.getCallingPid();
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                cancelRequestInternal(callingPid, token);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override // Binder call
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver result) {
             new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 7914531..6cc55a6 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,6 +16,13 @@
 
 package com.android.server.devicestate;
 
+import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.Binder;
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
@@ -27,10 +34,15 @@
  * Use with {@code adb shell cmd device_state ...}.
  */
 public class DeviceStateManagerShellCommand extends ShellCommand {
-    private final DeviceStateManagerService mInternal;
+    @Nullable
+    private static DeviceStateRequest sLastRequest;
+
+    private final DeviceStateManagerService mService;
+    private final DeviceStateManager mClient;
 
     public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
-        mInternal = service;
+        mService = service;
+        mClient = service.getContext().getSystemService(DeviceStateManager.class);
     }
 
     @Override
@@ -51,15 +63,15 @@
     }
 
     private void printState(PrintWriter pw) {
-        DeviceState committedState = mInternal.getCommittedState();
-        Optional<DeviceState> requestedState = mInternal.getRequestedState();
-        Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
+        DeviceState committedState = mService.getCommittedState();
+        Optional<DeviceState> baseState = mService.getBaseState();
+        Optional<DeviceState> overrideState = mService.getOverrideState();
 
         pw.println("Committed state: " + committedState);
-        if (requestedOverrideState.isPresent()) {
+        if (overrideState.isPresent()) {
             pw.println("----------------------");
-            pw.println("Base state: " + requestedState.orElse(null));
-            pw.println("Override state: " + requestedOverrideState.get());
+            pw.println("Base state: " + baseState.orElse(null));
+            pw.println("Override state: " + overrideState.get());
         }
     }
 
@@ -67,32 +79,51 @@
         final String nextArg = getNextArg();
         if (nextArg == null) {
             printState(pw);
-        } else if ("reset".equals(nextArg)) {
-            mInternal.clearOverrideState();
-        } else {
-            int requestedState;
-            try {
-                requestedState = Integer.parseInt(nextArg);
-            } catch (NumberFormatException e) {
-                getErrPrintWriter().println("Error: requested state should be an integer");
-                return -1;
-            }
-
-            boolean success = mInternal.setOverrideState(requestedState);
-            if (!success) {
-                getErrPrintWriter().println("Error: failed to set override state. Run:");
-                getErrPrintWriter().println("");
-                getErrPrintWriter().println("    print-states");
-                getErrPrintWriter().println("");
-                getErrPrintWriter().println("to get the list of currently supported device states");
-                return -1;
-            }
         }
+
+        final Context context = mService.getContext();
+        context.enforceCallingOrSelfPermission(
+                CONTROL_DEVICE_STATE,
+                "Permission required to request device state.");
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            if ("reset".equals(nextArg)) {
+                if (sLastRequest != null) {
+                    mClient.cancelRequest(sLastRequest);
+                    sLastRequest = null;
+                }
+            } else {
+                int requestedState = Integer.parseInt(nextArg);
+                DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+                mClient.requestState(request, null /* executor */, null /* callback */);
+                if (sLastRequest != null) {
+                    mClient.cancelRequest(sLastRequest);
+                }
+
+                sLastRequest = request;
+            }
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println("Error: requested state should be an integer");
+            return -1;
+        } catch (IllegalArgumentException e) {
+            getErrPrintWriter().println("Error: " + e.getMessage());
+            getErrPrintWriter().println("-------------------");
+            getErrPrintWriter().println("Run:");
+            getErrPrintWriter().println("");
+            getErrPrintWriter().println("    print-states");
+            getErrPrintWriter().println("");
+            getErrPrintWriter().println("to get the list of currently supported device states");
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+
         return 0;
     }
 
     private int runPrintStates(PrintWriter pw) {
-        DeviceState[] states = mInternal.getSupportedStates();
+        DeviceState[] states = mService.getSupportedStates();
         pw.print("Supported states: [\n");
         for (int i = 0; i < states.length; i++) {
             pw.print("  " + states[i] + ",\n");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e5151d8..01fee56 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -514,8 +514,8 @@
 
             DeviceStateManager deviceStateManager =
                     mContext.getSystemService(DeviceStateManager.class);
-            deviceStateManager.registerDeviceStateListener(new DeviceStateListener(),
-                    new HandlerExecutor(mHandler));
+            deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+                    new DeviceStateListener());
 
             scheduleTraversalLocked(false);
         }
@@ -2579,14 +2579,14 @@
         }
 
         @Override // Binder call
-        public void setTemporaryBrightness(float brightness) {
+        public void setTemporaryBrightness(int displayId, float brightness) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
                     "Permission required to set the display's brightness");
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+                    mDisplayPowerControllers.get(displayId)
                             .setTemporaryBrightness(brightness);
                 }
             } finally {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7e6a137..73ebb2e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -105,17 +105,18 @@
                 Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
                 return;
             }
-            SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken);
-            if (configs == null) {
-                // There are no valid configs for this device, so we can't use it
-                Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
+            SurfaceControl.DisplayMode[] displayModes =
+                    SurfaceControl.getDisplayModes(displayToken);
+            if (displayModes == null) {
+                // There are no valid modes for this device, so we can't use it
+                Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
                 return;
             }
-            int activeConfig = SurfaceControl.getActiveConfig(displayToken);
-            if (activeConfig < 0) {
-                // There is no active config, and for now we don't have the
+            int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken);
+            if (activeDisplayMode < 0) {
+                // There is no active mode, and for now we don't have the
                 // policy to set one.
-                Slog.w(TAG, "No active config found for display device " + physicalDisplayId);
+                Slog.w(TAG, "No active mode found for display device " + physicalDisplayId);
                 return;
             }
             int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
@@ -127,8 +128,8 @@
                         physicalDisplayId);
                 activeColorMode = Display.COLOR_MODE_INVALID;
             }
-            SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
-                    SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+            SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs =
+                    SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
             int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
             Display.HdrCapabilities hdrCapabilities =
                     SurfaceControl.getHdrCapabilities(displayToken);
@@ -136,13 +137,13 @@
             if (device == null) {
                 // Display was added.
                 final boolean isDefaultDisplay = mDevices.size() == 0;
-                device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
-                        configs, activeConfig, configSpecs, colorModes, activeColorMode,
+                device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes,
+                        activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode,
                         hdrCapabilities, isDefaultDisplay);
                 mDevices.put(physicalDisplayId, device);
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
-            } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
-                    configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+            } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode,
+                    modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) {
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
             }
         } else {
@@ -189,12 +190,12 @@
         // This is only set in the runnable returned from requestDisplayStateLocked.
         private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private int mDefaultModeId;
-        private int mDefaultConfigGroup;
+        private int mDefaultModeGroup;
         private int mActiveModeId;
         private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
                 new DisplayModeDirector.DesiredDisplayModeSpecs();
         private boolean mDisplayModeSpecsInvalid;
-        private int mActiveConfigId;
+        private int mActiveDisplayModeId;
         private int mActiveColorMode;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
@@ -204,7 +205,7 @@
         private boolean mSidekickActive;
         private SidekickInternal mSidekickInternal;
         private SurfaceControl.DisplayInfo mDisplayInfo;
-        private SurfaceControl.DisplayConfig[] mDisplayConfigs;
+        private SurfaceControl.DisplayMode[] mDisplayModes;
         private Spline mSystemBrightnessToNits;
         private Spline mNitsToHalBrightness;
         private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -213,15 +214,15 @@
                 new DisplayEventReceiver.FrameRateOverride[0];
 
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
-                SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
-                int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+                SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes,
+                int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
                 int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
                 boolean isDefaultDisplay) {
             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
             mPhysicalDisplayId = physicalDisplayId;
             mIsDefaultDisplay = isDefaultDisplay;
-            updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
-                    activeColorMode, hdrCapabilities);
+            updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs,
+                    colorModes, activeColorMode, hdrCapabilities);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
             mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay);
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
@@ -241,10 +242,11 @@
          * Returns true if there is a change.
          **/
         public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
-                SurfaceControl.DisplayConfig[] configs,
-                int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+                SurfaceControl.DisplayMode[] displayModes,
+                int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
                 int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
-            boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+            boolean changed = updateDisplayModesLocked(
+                    displayModes, activeDisplayModeId, modeSpecs);
             changed |= updateDisplayInfo(info);
             changed |= updateColorModesLocked(colorModes, activeColorMode);
             changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
@@ -255,35 +257,35 @@
             return changed;
         }
 
-        public boolean updateDisplayConfigsLocked(
-                SurfaceControl.DisplayConfig[] configs, int activeConfigId,
-                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
-            mDisplayConfigs = Arrays.copyOf(configs, configs.length);
-            mActiveConfigId = activeConfigId;
+        public boolean updateDisplayModesLocked(
+                SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
+                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+            mDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
+            mActiveDisplayModeId = activeDisplayModeId;
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<>();
             boolean modesAdded = false;
-            for (int i = 0; i < configs.length; i++) {
-                SurfaceControl.DisplayConfig config = configs[i];
+            for (int i = 0; i < displayModes.length; i++) {
+                SurfaceControl.DisplayMode mode = displayModes[i];
                 List<Float> alternativeRefreshRates = new ArrayList<>();
-                for (int j = 0; j < configs.length; j++) {
-                    SurfaceControl.DisplayConfig other = configs[j];
-                    boolean isAlternative = j != i && other.width == config.width
-                            && other.height == config.height
-                            && other.refreshRate != config.refreshRate
-                            && other.configGroup == config.configGroup;
+                for (int j = 0; j < displayModes.length; j++) {
+                    SurfaceControl.DisplayMode other = displayModes[j];
+                    boolean isAlternative = j != i && other.width == mode.width
+                            && other.height == mode.height
+                            && other.refreshRate != mode.refreshRate
+                            && other.group == mode.group;
                     if (isAlternative) {
-                        alternativeRefreshRates.add(configs[j].refreshRate);
+                        alternativeRefreshRates.add(displayModes[j].refreshRate);
                     }
                 }
                 Collections.sort(alternativeRefreshRates);
 
                 // First, check to see if we've already added a matching mode. Since not all
                 // configuration options are exposed via Display.Mode, it's possible that we have
-                // multiple DisplayConfigs that would generate the same Display.Mode.
+                // multiple DisplayModess that would generate the same Display.Mode.
                 boolean existingMode = false;
                 for (DisplayModeRecord record : records) {
-                    if (record.hasMatchingMode(config)
+                    if (record.hasMatchingMode(mode)
                             && refreshRatesEquals(alternativeRefreshRates,
                                     record.mMode.getAlternativeRefreshRates())) {
                         existingMode = true;
@@ -296,13 +298,13 @@
                 // If we haven't already added a mode for this configuration to the new set of
                 // supported modes then check to see if we have one in the prior set of supported
                 // modes to reuse.
-                DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates);
+                DisplayModeRecord record = findDisplayModeRecord(mode, alternativeRefreshRates);
                 if (record == null) {
                     float[] alternativeRates = new float[alternativeRefreshRates.size()];
                     for (int j = 0; j < alternativeRates.length; j++) {
                         alternativeRates[j] = alternativeRefreshRates.get(j);
                     }
-                    record = new DisplayModeRecord(config, alternativeRates);
+                    record = new DisplayModeRecord(mode, alternativeRates);
                     modesAdded = true;
                 }
                 records.add(record);
@@ -312,7 +314,7 @@
             DisplayModeRecord activeRecord = null;
             for (int i = 0; i < records.size(); i++) {
                 DisplayModeRecord record = records.get(i);
-                if (record.hasMatchingMode(configs[activeConfigId])) {
+                if (record.hasMatchingMode(displayModes[activeDisplayModeId])) {
                     activeRecord = record;
                     break;
                 }
@@ -334,20 +336,20 @@
             // Check whether surface flinger spontaneously changed display config specs out from
             // under us. If so, schedule a traversal to reapply our display config specs.
             if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
-                int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
-                // If we can't map the defaultConfig index to a mode, then the physical display
-                // configs must have changed, and the code below for handling changes to the
-                // list of available modes will take care of updating display config specs.
+                int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode);
+                // If we can't map the defaultMode index to a mode, then the physical display
+                // modes must have changed, and the code below for handling changes to the
+                // list of available modes will take care of updating display mode specs.
                 if (activeBaseMode != NO_DISPLAY_MODE_ID) {
                     if (mDisplayModeSpecs.baseModeId != activeBaseMode
                             || mDisplayModeSpecs.primaryRefreshRateRange.min
-                                    != configSpecs.primaryRefreshRateMin
+                                    != modeSpecs.primaryRefreshRateMin
                             || mDisplayModeSpecs.primaryRefreshRateRange.max
-                                    != configSpecs.primaryRefreshRateMax
+                                    != modeSpecs.primaryRefreshRateMax
                             || mDisplayModeSpecs.appRequestRefreshRateRange.min
-                                    != configSpecs.appRequestRefreshRateMin
+                                    != modeSpecs.appRequestRefreshRateMin
                             || mDisplayModeSpecs.appRequestRefreshRateRange.max
-                                    != configSpecs.appRequestRefreshRateMax) {
+                                    != modeSpecs.appRequestRefreshRateMax) {
                         mDisplayModeSpecsInvalid = true;
                         sendTraversalRequestLocked();
                     }
@@ -368,17 +370,17 @@
             // For a new display, we need to initialize the default mode ID.
             if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
             } else if (modesAdded && activeModeChanged) {
                 Slog.d(TAG, "New display modes are added and the active mode has changed, "
                         + "use active mode as default mode.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
-            } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) {
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+            } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
                 Slog.w(TAG, "Default display mode no longer available, using currently"
                         + " active mode as default.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
             }
 
             // Determine whether the display mode specs' base mode is still there.
@@ -518,11 +520,11 @@
             return true;
         }
 
-        private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config,
+        private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode,
                 List<Float> alternativeRefreshRates) {
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
-                if (record.hasMatchingMode(config)
+                if (record.hasMatchingMode(mode)
                         && refreshRatesEquals(alternativeRefreshRates,
                                 record.mMode.getAlternativeRefreshRates())) {
                     return record;
@@ -554,10 +556,10 @@
         @Override
         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
             if (mInfo == null) {
-                SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId];
+                SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId];
                 mInfo = new DisplayDeviceInfo();
-                mInfo.width = config.width;
-                mInfo.height = config.height;
+                mInfo.width = mode.width;
+                mInfo.height = mode.height;
                 mInfo.modeId = mActiveModeId;
                 mInfo.defaultModeId = mDefaultModeId;
                 mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -570,16 +572,16 @@
                     mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
                 }
                 mInfo.hdrCapabilities = mHdrCapabilities;
-                mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos;
-                mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos;
+                mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos;
+                mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos;
                 mInfo.state = mState;
                 mInfo.uniqueId = getUniqueId();
                 final DisplayAddress.Physical physicalAddress =
                         DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
                 mInfo.address = physicalAddress;
                 mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
-                mInfo.xDpi = config.xDpi;
-                mInfo.yDpi = config.yDpi;
+                mInfo.xDpi = mode.xDpi;
+                mInfo.yDpi = mode.yDpi;
                 mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
@@ -835,16 +837,16 @@
                 return;
             }
 
-            // Find the config Id based on the desired mode specs. In case there is more than one
-            // config matching the mode spec, prefer the one that is in the default config group.
-            // For now the default config group is taken from the active config when we got the
+            // Find the mode Id based on the desired mode specs. In case there is more than one
+            // mode matching the mode spec, prefer the one that is in the default mode group.
+            // For now the default config mode is taken from the active mode when we got the
             // hotplug event for the display. In the future we might want to change the default
-            // config based on vendor requirements.
-            // Note: We prefer the default config group over the current one as this is the config
+            // mode based on vendor requirements.
+            // Note: We prefer the default mode group over the current one as this is the mode
             // group the vendor prefers.
-            int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId,
-                    mDefaultConfigGroup);
-            if (baseConfigId < 0) {
+            int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+                    mDefaultModeGroup);
+            if (baseModeId < 0) {
                 // When a display is hotplugged, it's possible for a mode to be removed that was
                 // previously valid. Because of the way display changes are propagated through the
                 // framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -862,7 +864,7 @@
                 getHandler().sendMessage(PooledLambda.obtainMessage(
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
-                        new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
+                        new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
                                 mDisplayModeSpecs.allowGroupSwitching,
                                 mDisplayModeSpecs.primaryRefreshRateRange.min,
                                 mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -872,13 +874,13 @@
         }
 
         private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
-                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
             // Do not lock when calling these SurfaceControl methods because they are sync
             // operations that may block for a while when setting display power mode.
-            SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
-            final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+            SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
+            final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken);
             synchronized (getSyncRoot()) {
-                if (updateActiveModeLocked(activePhysIndex)) {
+                if (updateActiveModeLocked(activeMode)) {
                     updateDeviceInfoLocked();
                 }
             }
@@ -889,8 +891,8 @@
             updateDeviceInfoLocked();
         }
 
-        public void onActiveDisplayConfigChangedLocked(int configId) {
-            if (updateActiveModeLocked(configId)) {
+        public void onActiveDisplayModeChangedLocked(int modeId) {
+            if (updateActiveModeLocked(modeId)) {
                 updateDeviceInfoLocked();
             }
         }
@@ -902,15 +904,15 @@
             }
         }
 
-        public boolean updateActiveModeLocked(int activeConfigId) {
-            if (mActiveConfigId == activeConfigId) {
+        public boolean updateActiveModeLocked(int activeModeId) {
+            if (mActiveDisplayModeId == activeModeId) {
                 return false;
             }
-            mActiveConfigId = activeConfigId;
-            mActiveModeId = findMatchingModeIdLocked(activeConfigId);
+            mActiveDisplayModeId = activeModeId;
+            mActiveModeId = findMatchingModeIdLocked(activeModeId);
             if (mActiveModeId == NO_DISPLAY_MODE_ID) {
-                Slog.w(TAG, "In unknown mode after setting allowed configs"
-                        + ", activeConfigId=" + mActiveConfigId);
+                Slog.w(TAG, "In unknown mode after setting allowed modes"
+                        + ", activeModeId=" + mActiveDisplayModeId);
             }
             return true;
         }
@@ -990,7 +992,7 @@
             pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
             pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
             pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
-            pw.println("mActiveConfigId=" + mActiveConfigId);
+            pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId);
             pw.println("mActiveModeId=" + mActiveModeId);
             pw.println("mActiveColorMode=" + mActiveColorMode);
             pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -1002,9 +1004,9 @@
             pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
             pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
             pw.println("mDisplayInfo=" + mDisplayInfo);
-            pw.println("mDisplayConfigs=");
-            for (int i = 0; i < mDisplayConfigs.length; i++) {
-                pw.println("  " + mDisplayConfigs[i]);
+            pw.println("mDisplayModes=");
+            for (int i = 0; i < mDisplayModes.length; i++) {
+                pw.println("  " + mDisplayModes[i]);
             }
             pw.println("mSupportedModes=");
             for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -1014,37 +1016,37 @@
             pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
         }
 
-        private int findDisplayConfigIdLocked(int modeId, int configGroup) {
-            int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID;
+        private int findDisplayModeIdLocked(int modeId, int modeGroup) {
+            int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
             DisplayModeRecord record = mSupportedModes.get(modeId);
             if (record != null) {
-                for (int i = 0; i < mDisplayConfigs.length; i++) {
-                    SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
-                    if (record.hasMatchingMode(config)) {
-                        if (matchingConfigId
-                                == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) {
-                            matchingConfigId = i;
+                for (int i = 0; i < mDisplayModes.length; i++) {
+                    SurfaceControl.DisplayMode mode = mDisplayModes[i];
+                    if (record.hasMatchingMode(mode)) {
+                        if (matchingModeId
+                                == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
+                            matchingModeId = i;
                         }
 
-                        // Prefer to return a config that matches the configGroup
-                        if (config.configGroup == configGroup) {
+                        // Prefer to return a mode that matches the modeGroup
+                        if (mode.group == modeGroup) {
                             return i;
                         }
                     }
                 }
             }
-            return matchingConfigId;
+            return matchingModeId;
         }
 
-        private int findMatchingModeIdLocked(int configId) {
-            if (configId < 0 || configId >= mDisplayConfigs.length) {
-                Slog.e(TAG, "Invalid display config index " + configId);
+        private int findMatchingModeIdLocked(int modeId) {
+            if (modeId < 0 || modeId >= mDisplayModes.length) {
+                Slog.e(TAG, "Invalid display config index " + modeId);
                 return NO_DISPLAY_MODE_ID;
             }
-            SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
+            SurfaceControl.DisplayMode mode = mDisplayModes[modeId];
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
-                if (record.hasMatchingMode(config)) {
+                if (record.hasMatchingMode(mode)) {
                     return record.mMode.getModeId();
                 }
             }
@@ -1091,30 +1093,29 @@
     }
 
     /**
-     * Keeps track of a display configuration.
+     * Keeps track of a display mode.
      */
     private static final class DisplayModeRecord {
         public final Display.Mode mMode;
 
-        DisplayModeRecord(SurfaceControl.DisplayConfig config,
+        DisplayModeRecord(SurfaceControl.DisplayMode mode,
                 float[] alternativeRefreshRates) {
-            mMode = createMode(config.width, config.height, config.refreshRate,
+            mMode = createMode(mode.width, mode.height, mode.refreshRate,
                     alternativeRefreshRates);
         }
 
         /**
-         * Returns whether the mode generated by the given DisplayConfig matches the mode
+         * Returns whether the mode generated by the given DisplayModes matches the mode
          * contained by the record modulo mode ID.
          *
-         * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just
+         * Note that this doesn't necessarily mean that the DisplayModes are identical, just
          * that they generate identical modes.
          */
-        public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) {
-            int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
-            int configRefreshRate = Float.floatToIntBits(config.refreshRate);
-            return mMode.getPhysicalWidth() == config.width
-                    && mMode.getPhysicalHeight() == config.height
-                    && modeRefreshRate == configRefreshRate;
+        public boolean hasMatchingMode(SurfaceControl.DisplayMode mode) {
+            return mMode.getPhysicalWidth() == mode.width
+                    && mMode.getPhysicalHeight() == mode.height
+                    && Float.floatToIntBits(mMode.getRefreshRate())
+                        == Float.floatToIntBits(mode.refreshRate);
         }
 
         public String toString() {
@@ -1131,7 +1132,7 @@
 
     public interface DisplayEventListener {
         void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
-        void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+        void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId);
         void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
                 DisplayEventReceiver.FrameRateOverride[] overrides);
 
@@ -1141,7 +1142,7 @@
         private final DisplayEventListener mListener;
         ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
             super(looper, VSYNC_SOURCE_APP,
-                    EVENT_REGISTRATION_CONFIG_CHANGED_FLAG
+                    EVENT_REGISTRATION_MODE_CHANGED_FLAG
                             | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG);
             mListener = listener;
         }
@@ -1152,8 +1153,8 @@
         }
 
         @Override
-        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
-            mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+        public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+            mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId);
         }
 
         @Override
@@ -1176,23 +1177,23 @@
         }
 
         @Override
-        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+        public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
             if (DEBUG) {
-                Slog.d(TAG, "onConfigChanged("
+                Slog.d(TAG, "onModeChanged("
                         + "timestampNanos=" + timestampNanos
                         + ", physicalDisplayId=" + physicalDisplayId
-                        + ", configId=" + configId + ")");
+                        + ", modeId=" + modeId + ")");
             }
             synchronized (getSyncRoot()) {
                 LocalDisplayDevice device = mDevices.get(physicalDisplayId);
                 if (device == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Received config change for unhandled physical display: "
+                        Slog.d(TAG, "Received mode change for unhandled physical display: "
                                 + "physicalDisplayId=" + physicalDisplayId);
                     }
                     return;
                 }
-                device.onActiveDisplayConfigChangedLocked(configId);
+                device.onActiveDisplayModeChangedLocked(modeId);
             }
         }
 
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 3e2b5ab..1b27572 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -345,6 +345,7 @@
             synchronized (mSerializedFontMapLock) {
                 mSerializedFontMap = serializeFontMap;
             }
+            return;
         } catch (IOException | ErrnoException e) {
             Slog.w(TAG, "Failed to serialize updatable font map. "
                     + "Retrying with system image fonts.", e);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0ae1994..bdf92ca 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -541,7 +541,7 @@
     private void bootCompleted() {
         // on boot, if device is interactive, set HDMI CEC state as powered on as well
         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
-            onWakeUp(WAKE_UP_BOOT_UP);
+            mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
         }
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2e4200c..4e97411 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -57,6 +57,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.InputEventInjectionResult;
 import android.os.InputEventInjectionSync;
 import android.os.LocaleList;
@@ -64,6 +65,7 @@
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.Process;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
@@ -77,6 +79,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.IInputFilter;
 import android.view.IInputFilterHost;
@@ -100,6 +103,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -221,13 +225,16 @@
     private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
     private int mNextVibratorTokenValue;
 
+    // List of currently registered vibrator state changed listeners by device id.
+    @GuardedBy("mVibratorLock")
+    private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
+            new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
+    // List of vibrator states by device id.
+    @GuardedBy("mVibratorLock")
+    private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+
     // State for lid switch
-    // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
-    // are delivered in order. For ex, when a new lid switch callback is registered the lock is held
-    // while the callback is processing the initial lid switch event which guarantees that any
-    // events that occur at the same time are delivered after the callback has returned.
     private final Object mLidSwitchLock = new Object();
-    @GuardedBy("mLidSwitchLock")
     private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
 
     // State for the currently installed input filter.
@@ -375,6 +382,9 @@
     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
     public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
 
+    /** Indicates an open state for the lid switch. */
+    public static final int SW_STATE_LID_OPEN = 0;
+
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
     final boolean mUseDevInputEventForAudioJack;
 
@@ -410,18 +420,13 @@
     }
 
     void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+        boolean lidOpen;
         synchronized (mLidSwitchLock) {
             mLidSwitchCallbacks.add(callback);
-
-            // Skip triggering the initial callback if the system is not yet ready as the switch
-            // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in
-            // systemRunning().
-            if (mSystemReady) {
-                boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
-                        == KEY_STATE_UP;
-                callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
-            }
+            lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+                    == SW_STATE_LID_OPEN;
         }
+        callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
     }
 
     void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
@@ -469,18 +474,7 @@
         }
         mNotificationManager = (NotificationManager)mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
-
-        synchronized (mLidSwitchLock) {
-            mSystemReady = true;
-
-            // Send the initial lid switch state to any callback registered before the system was
-            // ready.
-            int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
-            for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
-                LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
-                callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
-            }
-        }
+        mSystemReady = true;
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -2008,6 +2002,92 @@
         }
     }
 
+    // Native callback.
+    private void notifyVibratorState(int deviceId, boolean isOn) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
+        }
+        synchronized (mVibratorLock) {
+            mIsVibrating.put(deviceId, isOn);
+            notifyVibratorStateListenersLocked(deviceId);
+        }
+    }
+
+    @GuardedBy("mVibratorLock")
+    private void notifyVibratorStateListenersLocked(int deviceId) {
+        if (!mVibratorStateListeners.contains(deviceId)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener.");
+            }
+            return;
+        }
+        RemoteCallbackList<IVibratorStateListener> listeners =
+                mVibratorStateListeners.get(deviceId);
+        final int length = listeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i));
+            }
+        } finally {
+            listeners.finishBroadcast();
+        }
+    }
+
+    @GuardedBy("mVibratorLock")
+    private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) {
+        try {
+            listener.onVibrating(mIsVibrating.get(deviceId));
+        } catch (RemoteException | RuntimeException e) {
+            Slog.e(TAG, "Vibrator state listener failed to call", e);
+        }
+    }
+
+    @Override // Binder call
+    public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        Preconditions.checkNotNull(listener, "listener must not be null");
+
+        RemoteCallbackList<IVibratorStateListener> listeners;
+        synchronized (mVibratorLock) {
+            if (!mVibratorStateListeners.contains(deviceId)) {
+                listeners = new RemoteCallbackList<>();
+                mVibratorStateListeners.put(deviceId, listeners);
+            } else {
+                listeners = mVibratorStateListeners.get(deviceId);
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!listeners.register(listener)) {
+                    Slog.e(TAG, "Could not register vibrator state listener " + listener);
+                    return false;
+                }
+                // Notify its callback after new client registered.
+                notifyVibratorStateListenerLocked(deviceId, listener);
+                return true;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    @Override // Binder call
+    public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        synchronized (mVibratorLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mVibratorStateListeners.contains(deviceId)) {
+                    Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist");
+                    return false;
+                }
+                RemoteCallbackList<IVibratorStateListener> listeners =
+                        mVibratorStateListeners.get(deviceId);
+                return listeners.unregister(listener);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
     // Binder call
     @Override
     public int getBatteryStatus(int deviceId) {
@@ -2251,13 +2331,14 @@
 
         if ((switchMask & SW_LID_BIT) != 0) {
             final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
+
+            ArrayList<LidSwitchCallback> callbacksCopy;
             synchronized (mLidSwitchLock) {
-                if (mSystemReady) {
-                    for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
-                        LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i);
-                        callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
-                    }
-                }
+                callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+            }
+            for (int i = 0; i < callbacksCopy.size(); i++) {
+                LidSwitchCallback callbacks = callbacksCopy.get(i);
+                callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
             }
         }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..1a4c8b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -300,45 +299,6 @@
     private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
             "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
 
-    /**
-     * Debug flag for overriding runtime {@link SystemProperties}.
-     */
-    @AnyThread
-    private static final class DebugFlag {
-        private static final Object LOCK = new Object();
-        private final String mKey;
-        private final boolean mDefaultValue;
-        @GuardedBy("LOCK")
-        private boolean mValue;
-
-        public DebugFlag(String key, boolean defaultValue) {
-            mKey = key;
-            mDefaultValue = defaultValue;
-            mValue = SystemProperties.getBoolean(key, defaultValue);
-        }
-
-        void refresh() {
-            synchronized (LOCK) {
-                mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
-            }
-        }
-
-        boolean value() {
-            synchronized (LOCK) {
-                return mValue;
-            }
-        }
-    }
-
-    /**
-     * Debug flags that can be overridden using "adb shell setprop <key>"
-     * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
-     */
-    private static final class DebugFlags {
-        static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
-                new DebugFlag("debug.optimize_startinput", false);
-    }
-
     @UserIdInt
     private int mLastSwitchUserId;
 
@@ -2586,7 +2546,7 @@
                             + mCurTokenDisplayId);
                 }
                 mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD,
-                        mCurTokenDisplayId);
+                        mCurTokenDisplayId, null /* options */);
             } catch (RemoteException e) {
             }
             return new InputBindResult(
@@ -3687,12 +3647,9 @@
                     }
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
-                        || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+                } else {
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else {
-                    res = InputBindResult.NO_EDITOR;
                 }
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
@@ -5270,6 +5227,8 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
         boolean asProto = false;
         for (int argIndex = 0; argIndex < args.length; argIndex++) {
             if (args[argIndex].equals(PROTO_ARG)) {
@@ -5292,8 +5251,6 @@
     }
 
     private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
             dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
@@ -5467,10 +5424,6 @@
         @BinderThread
         @ShellCommandResult
         private int onCommandWithSystemIdentity(@Nullable String cmd) {
-            if ("refresh_debug_properties".equals(cmd)) {
-                return refreshDebugProperties();
-            }
-
             if ("get-last-switch-user-id".equals(cmd)) {
                 return mService.getLastSwitchUserId(this);
             }
@@ -5505,13 +5458,6 @@
         }
 
         @BinderThread
-        @ShellCommandResult
-        private int refreshDebugProperties() {
-            DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
-            return ShellCommandResult.SUCCESS;
-        }
-
-        @BinderThread
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6fec906..1dd3d41 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1309,7 +1309,8 @@
                 final Binder token = new Binder();
                 Binder.withCleanCallingIdentity(
                         PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
-                                mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId));
+                                mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
+                                null /* options */));
                 mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
                 return token;
             }
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 142f64f..28dc516 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -66,6 +66,7 @@
 import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
@@ -78,6 +79,7 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.stats.location.LocationStatsEnums;
+import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 
@@ -86,6 +88,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.location.eventlog.LocationEventLog;
 import com.android.server.location.geofence.GeofenceManager;
 import com.android.server.location.geofence.GeofenceProxy;
 import com.android.server.location.gnss.GnssConfiguration;
@@ -97,7 +100,6 @@
 import com.android.server.location.injector.EmergencyHelper;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
 import com.android.server.location.injector.LocationPermissionsHelper;
 import com.android.server.location.injector.LocationPowerSaveModeHelper;
 import com.android.server.location.injector.LocationUsageLogger;
@@ -146,9 +148,10 @@
 
         public Lifecycle(Context context) {
             super(context);
+            LocationEventLog eventLog = new LocationEventLog();
             mUserInfoHelper = new LifecycleUserInfoHelper(context);
-            mSystemInjector = new SystemInjector(context, mUserInfoHelper);
-            mService = new LocationManagerService(context, mSystemInjector);
+            mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog);
+            mService = new LocationManagerService(context, mSystemInjector, eventLog);
         }
 
         @Override
@@ -158,7 +161,7 @@
             // client caching behavior is only enabled after seeing the first invalidate
             LocationManager.invalidateLocalLocationEnabledCaches();
             // disable caching for our own process
-            Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
+            Objects.requireNonNull(getContext().getSystemService(LocationManager.class))
                     .disableLocalLocationEnabledCaches();
         }
 
@@ -220,6 +223,7 @@
 
     private final Context mContext;
     private final Injector mInjector;
+    private final LocationEventLog mEventLog;
     private final LocalService mLocalService;
 
     private final GeofenceManager mGeofenceManager;
@@ -244,10 +248,10 @@
     private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
             new CopyOnWriteArrayList<>();
 
-    LocationManagerService(Context context, Injector injector) {
+    LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mInjector = injector;
-
+        mEventLog = eventLog;
         mLocalService = new LocalService();
         LocalServices.addService(LocationManagerInternal.class, mLocalService);
 
@@ -255,7 +259,7 @@
 
         // set up passive provider first since it will be required for all other location providers,
         // which are loaded later once the system is ready.
-        mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
+        mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog);
         addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
 
         // TODO: load the gps provider here as well, which will require refactoring
@@ -296,7 +300,7 @@
             }
 
             LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
-                    providerName, mPassiveManager);
+                    mEventLog, providerName, mPassiveManager);
             addLocationProviderManager(manager, null);
             return manager;
         }
@@ -340,7 +344,7 @@
                 com.android.internal.R.string.config_networkLocationProviderPackageName);
         if (networkProvider != null) {
             LocationProviderManager networkManager = new LocationProviderManager(mContext,
-                    mInjector, NETWORK_PROVIDER, mPassiveManager);
+                    mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager);
             addLocationProviderManager(networkManager, networkProvider);
         } else {
             Log.w(TAG, "no network location provider found");
@@ -359,7 +363,7 @@
                 com.android.internal.R.string.config_fusedLocationProviderPackageName);
         if (fusedProvider != null) {
             LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
-                    FUSED_PROVIDER, mPassiveManager);
+                    mEventLog, FUSED_PROVIDER, mPassiveManager);
             addLocationProviderManager(fusedManager, fusedProvider);
         } else {
             Log.wtf(TAG, "no fused location provider found");
@@ -374,7 +378,7 @@
             mGnssManagerService.onSystemReady();
 
             LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
-                    GPS_PROVIDER, mPassiveManager);
+                    mEventLog, GPS_PROVIDER, mPassiveManager);
             addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
         }
 
@@ -430,7 +434,7 @@
             Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
         }
 
-        mInjector.getLocationEventLog().logLocationEnabled(userId, enabled);
+        mEventLog.logLocationEnabled(userId, enabled);
 
         Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
                 .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
@@ -882,6 +886,20 @@
     }
 
     @Override
+    public void addProviderRequestListener(IProviderRequestListener listener) {
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.addProviderRequestListener(listener);
+        }
+    }
+
+    @Override
+    public void removeProviderRequestListener(IProviderRequestListener listener) {
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.removeProviderRequestListener(listener);
+        }
+    }
+
+    @Override
     public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
         if (mGnssManagerService != null) {
             mGnssManagerService.injectGnssMeasurementCorrections(corrections);
@@ -1178,9 +1196,27 @@
 
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
 
-        if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
-            mGnssManagerService.dump(fd, ipw, args);
-            return;
+        if (args.length > 0) {
+            LocationProviderManager manager = getLocationProviderManager(args[0]);
+            if (manager != null) {
+                ipw.println("Provider:");
+                ipw.increaseIndent();
+                manager.dump(fd, ipw, args);
+                ipw.decreaseIndent();
+
+                ipw.println("Event Log:");
+                ipw.increaseIndent();
+                mEventLog.iterate(manager.getName(), ipw::println);
+                ipw.decreaseIndent();
+                return;
+            }
+
+            if ("--gnssmetrics".equals(args[0])) {
+                if (mGnssManagerService != null) {
+                    mGnssManagerService.dump(fd, ipw, args);
+                }
+                return;
+            }
         }
 
         ipw.println("Location Manager State:");
@@ -1212,6 +1248,25 @@
         }
         ipw.decreaseIndent();
 
+        ipw.println("Historical Aggregate Location Provider Data:");
+        ipw.increaseIndent();
+        ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+                mEventLog.copyAggregateStats();
+        for (int i = 0; i < aggregateStats.size(); i++) {
+            ipw.println(aggregateStats.keyAt(i));
+            ipw.increaseIndent();
+            ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+                    aggregateStats.valueAt(i);
+            for (int j = 0; j < providerStats.size(); j++) {
+                ipw.print(providerStats.keyAt(j));
+                ipw.print(": ");
+                providerStats.valueAt(j).updateTotals();
+                ipw.println(providerStats.valueAt(j));
+            }
+            ipw.decreaseIndent();
+        }
+        ipw.decreaseIndent();
+
         if (mGnssManagerService != null) {
             ipw.println("GNSS Manager:");
             ipw.increaseIndent();
@@ -1226,7 +1281,7 @@
 
         ipw.println("Event Log:");
         ipw.increaseIndent();
-        mInjector.getLocationEventLog().iterate(ipw::println);
+        mEventLog.iterate(ipw::println);
         ipw.decreaseIndent();
     }
 
@@ -1305,7 +1360,6 @@
         private final Context mContext;
 
         private final UserInfoHelper mUserInfoHelper;
-        private final LocationEventLog mLocationEventLog;
         private final AlarmHelper mAlarmHelper;
         private final SystemAppOpsHelper mAppOpsHelper;
         private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1324,19 +1378,17 @@
         @GuardedBy("this")
         private boolean mSystemReady;
 
-        SystemInjector(Context context, UserInfoHelper userInfoHelper) {
+        SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) {
             mContext = context;
 
             mUserInfoHelper = userInfoHelper;
-            mLocationEventLog = new LocationEventLog();
             mAlarmHelper = new SystemAlarmHelper(context);
             mAppOpsHelper = new SystemAppOpsHelper(context);
             mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
                     mAppOpsHelper);
             mSettingsHelper = new SystemSettingsHelper(context);
             mAppForegroundHelper = new SystemAppForegroundHelper(context);
-            mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context,
-                    mLocationEventLog);
+            mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
             mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
             mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
             mLocationUsageLogger = new LocationUsageLogger();
@@ -1415,11 +1467,6 @@
         }
 
         @Override
-        public LocationEventLog getLocationEventLog() {
-            return mLocationEventLog;
-        }
-
-        @Override
         public LocationUsageLogger getLocationUsageLogger() {
             return mLocationUsageLogger;
         }
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 77%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..12dd3e6 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
 
+import android.annotation.Nullable;
 import android.os.SystemClock;
 import android.util.TimeUtils;
 
 import com.android.internal.util.Preconditions;
 
-import java.util.ListIterator;
+import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.function.Consumer;
 
@@ -35,6 +36,7 @@
         boolean isFiller();
         long getTimeDeltaMs();
         String getLogString();
+        boolean filter(@Nullable String filter);
     }
 
     private static final class FillerEvent implements Log {
@@ -62,6 +64,11 @@
         public String getLogString() {
             throw new AssertionError();
         }
+
+        @Override
+        public boolean filter(String filter) {
+            return false;
+        }
     }
 
     /**
@@ -87,6 +94,11 @@
         public final long getTimeDeltaMs() {
             return Integer.toUnsignedLong(mTimeDelta);
         }
+
+        @Override
+        public boolean filter(String filter) {
+            return false;
+        }
     }
 
     // circular buffer of log entries
@@ -198,6 +210,17 @@
         }
     }
 
+    /**
+     * Iterates over the event log, passing each filter-matching log string to the given
+     * consumer.
+     */
+    public synchronized void iterate(String filter, Consumer<String> consumer) {
+        LogIterator it = new LogIterator(filter);
+        while (it.hasNext()) {
+            consumer.accept(it.next());
+        }
+    }
+
     // returns the index of the first element
     private int startIndex() {
         return wrapIndex(mLogEndIndex - mLogSize);
@@ -205,12 +228,13 @@
 
     // returns the index after this one
     private int incrementIndex(int index) {
-        return wrapIndex(index + 1);
-    }
-
-    // returns the index before this one
-    private int decrementIndex(int index) {
-        return wrapIndex(index - 1);
+        if (index == -1) {
+            return startIndex();
+        } else if (index >= 0) {
+            return wrapIndex(index + 1);
+        } else {
+            throw new IllegalArgumentException();
+        }
     }
 
     // rolls over the given index if necessary
@@ -219,7 +243,9 @@
         return (index % mLog.length + mLog.length) % mLog.length;
     }
 
-    private class LogIterator implements ListIterator<String> {
+    private class LogIterator implements Iterator<String> {
+
+        private final @Nullable String mFilter;
 
         private final long mSystemTimeDeltaMs;
 
@@ -228,10 +254,17 @@
         private int mCount;
 
         LogIterator() {
+            this(null);
+        }
+
+        LogIterator(@Nullable String filter) {
+            mFilter = filter;
             mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
             mCurrentRealtimeMs = mStartRealtimeMs;
-            mIndex = startIndex();
-            mCount = 0;
+            mIndex = -1;
+            mCount = -1;
+
+            increment();
         }
 
         @Override
@@ -239,75 +272,17 @@
             return mCount < mLogSize;
         }
 
-        @Override
-        public boolean hasPrevious() {
-            return mCount > 0;
-        }
-
-        @Override
-        // return then increment
         public String next() {
             if (!hasNext()) {
                 throw new NoSuchElementException();
             }
 
             Log log = mLog[mIndex];
-            long nextDeltaMs = log.getTimeDeltaMs();
-            long realtimeMs = mCurrentRealtimeMs + nextDeltaMs;
+            long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
 
-            // calculate next index, skipping filler events
-            do {
-                mCurrentRealtimeMs += nextDeltaMs;
-                mIndex = incrementIndex(mIndex);
-                if (++mCount < mLogSize) {
-                    nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
-                }
-            } while (mCount < mLogSize && mLog[mIndex].isFiller());
+            increment();
 
-            return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
-        }
-
-        @Override
-        // decrement then return
-        public String previous() {
-            Log log;
-            long currentDeltaMs;
-            long realtimeMs;
-
-            // calculate previous index, skipping filler events with MAX_TIME_DELTA
-            do {
-                if (!hasPrevious()) {
-                    throw new NoSuchElementException();
-                }
-
-                mIndex = decrementIndex(mIndex);
-                mCount--;
-
-                log = mLog[mIndex];
-                realtimeMs = mCurrentRealtimeMs;
-
-                if (mCount > 0) {
-                    currentDeltaMs = log.getTimeDeltaMs();
-                    mCurrentRealtimeMs -= currentDeltaMs;
-                }
-            } while (mCount >= 0 && log.isFiller());
-
-            return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
-        }
-
-        @Override
-        public int nextIndex() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int previousIndex() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void add(String s) {
-            throw new UnsupportedOperationException();
+            return getTimePrefix(timeMs) + log.getLogString();
         }
 
         @Override
@@ -315,9 +290,16 @@
             throw new UnsupportedOperationException();
         }
 
-        @Override
-        public void set(String s) {
-            throw new UnsupportedOperationException();
+        private void increment() {
+            long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs();
+            do {
+                mCurrentRealtimeMs += nextDeltaMs;
+                mIndex = incrementIndex(mIndex);
+                if (++mCount < mLogSize) {
+                    nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
+                }
+            } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null
+                    && !mLog[mIndex].filter(mFilter))));
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
new file mode 100644
index 0000000..67060fc
--- /dev/null
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -0,0 +1,502 @@
+/*
+ * 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.location.eventlog;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.util.TimeUtils.formatDuration;
+
+import static com.android.server.location.LocationManagerService.D;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.annotation.Nullable;
+import android.location.LocationRequest;
+import android.location.provider.ProviderRequest;
+import android.location.util.identity.CallerIdentity;
+import android.os.Build;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+/** In memory event log for location events. */
+public class LocationEventLog extends LocalEventLog {
+
+    private static int getLogSize() {
+        if (Build.IS_DEBUGGABLE || D) {
+            return 500;
+        } else {
+            return 200;
+        }
+    }
+
+    private static final int EVENT_LOCATION_ENABLED = 1;
+    private static final int EVENT_PROVIDER_ENABLED = 2;
+    private static final int EVENT_PROVIDER_MOCKED = 3;
+    private static final int EVENT_PROVIDER_REGISTER_CLIENT = 4;
+    private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 5;
+    private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
+    private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
+    private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
+    private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
+
+    @GuardedBy("mAggregateStats")
+    private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
+
+    public LocationEventLog() {
+        super(getLogSize());
+        mAggregateStats = new ArrayMap<>(4);
+    }
+
+    public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+        synchronized (mAggregateStats) {
+            ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+                    mAggregateStats);
+            for (int i = 0; i < copy.size(); i++) {
+                copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
+            }
+            return copy;
+        }
+    }
+
+    private AggregateStats getAggregateStats(String provider, String packageName) {
+        synchronized (mAggregateStats) {
+            ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+            if (packageMap == null) {
+                packageMap = new ArrayMap<>(2);
+                mAggregateStats.put(provider, packageMap);
+            }
+            AggregateStats stats = packageMap.get(packageName);
+            if (stats == null) {
+                stats = new AggregateStats();
+                packageMap.put(packageName, stats);
+            }
+            return stats;
+        }
+    }
+
+    /** Logs a location enabled/disabled event. */
+    public void logLocationEnabled(int userId, boolean enabled) {
+        addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
+    }
+
+    /** Logs a location provider enabled/disabled event. */
+    public void logProviderEnabled(String provider, int userId, boolean enabled) {
+        addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
+    }
+
+    /** Logs a location provider being replaced/unreplaced by a mock provider. */
+    public void logProviderMocked(String provider, boolean mocked) {
+        addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
+    }
+
+    /** Logs a new client registration for a location provider. */
+    public void logProviderClientRegistered(String provider, CallerIdentity identity,
+            LocationRequest request) {
+        addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
+        getAggregateStats(provider, identity.getPackageName())
+                .markRequestAdded(request.getIntervalMillis());
+    }
+
+    /** Logs a client unregistration for a location provider. */
+    public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
+        addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
+        getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+    }
+
+    /** Logs a client for a location provider entering the active state. */
+    public void logProviderClientActive(String provider, CallerIdentity identity) {
+        getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+    }
+
+    /** Logs a client for a location provider leaving the active state. */
+    public void logProviderClientInactive(String provider, CallerIdentity identity) {
+        getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+    }
+
+    /** Logs a client for a location provider entering the foreground state. */
+    public void logProviderClientForeground(String provider, CallerIdentity identity) {
+        getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+    }
+
+    /** Logs a client for a location provider leaving the foreground state. */
+    public void logProviderClientBackground(String provider, CallerIdentity identity) {
+        getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
+    }
+
+    /** Logs a change to the provider request for a location provider. */
+    public void logProviderUpdateRequest(String provider, ProviderRequest request) {
+        addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
+    }
+
+    /** Logs a new incoming location for a location provider. */
+    public void logProviderReceivedLocations(String provider, int numLocations) {
+        if (Build.IS_DEBUGGABLE || D) {
+            addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
+        }
+    }
+
+    /** Logs a location deliver for a client of a location provider. */
+    public void logProviderDeliveredLocations(String provider, int numLocations,
+            CallerIdentity identity) {
+        if (Build.IS_DEBUGGABLE || D) {
+            addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
+        }
+    }
+
+    /** Logs that the location power save mode has changed. */
+    public void logLocationPowerSaveMode(
+            @LocationPowerSaveMode int locationPowerSaveMode) {
+        addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
+    }
+
+    @Override
+    protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
+        switch (event) {
+            case EVENT_LOCATION_ENABLED:
+                return new LocationEnabledEvent(timeDelta, (Integer) args[1], (Boolean) args[2]);
+            case EVENT_PROVIDER_ENABLED:
+                return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
+                        (Boolean) args[2]);
+            case EVENT_PROVIDER_MOCKED:
+                return new ProviderMockedEvent(timeDelta, (String) args[0], (Boolean) args[1]);
+            case EVENT_PROVIDER_REGISTER_CLIENT:
+                return new ProviderRegisterEvent(timeDelta, (String) args[0], true,
+                        (CallerIdentity) args[1], (LocationRequest) args[2]);
+            case EVENT_PROVIDER_UNREGISTER_CLIENT:
+                return new ProviderRegisterEvent(timeDelta, (String) args[0], false,
+                        (CallerIdentity) args[1], null);
+            case EVENT_PROVIDER_UPDATE_REQUEST:
+                return new ProviderUpdateEvent(timeDelta, (String) args[0],
+                        (ProviderRequest) args[1]);
+            case EVENT_PROVIDER_RECEIVE_LOCATION:
+                return new ProviderReceiveLocationEvent(timeDelta, (String) args[0],
+                        (Integer) args[1]);
+            case EVENT_PROVIDER_DELIVER_LOCATION:
+                return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
+                        (Integer) args[1], (CallerIdentity) args[2]);
+            case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
+                return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    private abstract static class ProviderEvent extends LogEvent {
+
+        protected final String mProvider;
+
+        protected ProviderEvent(long timeDelta, String provider) {
+            super(timeDelta);
+            mProvider = provider;
+        }
+
+        @Override
+        public boolean filter(String filter) {
+            return mProvider.equals(filter);
+        }
+    }
+
+    private static final class ProviderEnabledEvent extends ProviderEvent {
+
+        private final int mUserId;
+        private final boolean mEnabled;
+
+        protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
+                boolean enabled) {
+            super(timeDelta, provider);
+            mUserId = userId;
+            mEnabled = enabled;
+        }
+
+        @Override
+        public String getLogString() {
+            return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
+                    : "disabled");
+        }
+    }
+
+    private static final class ProviderMockedEvent extends ProviderEvent {
+
+        private final boolean mMocked;
+
+        protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
+            super(timeDelta, provider);
+            mMocked = mocked;
+        }
+
+        @Override
+        public String getLogString() {
+            if (mMocked) {
+                return mProvider + " provider added mock provider override";
+            } else {
+                return mProvider + " provider removed mock provider override";
+            }
+        }
+    }
+
+    private static final class ProviderRegisterEvent extends ProviderEvent {
+
+        private final boolean mRegistered;
+        private final CallerIdentity mIdentity;
+        @Nullable private final LocationRequest mLocationRequest;
+
+        private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
+                CallerIdentity identity, @Nullable LocationRequest locationRequest) {
+            super(timeDelta, provider);
+            mRegistered = registered;
+            mIdentity = identity;
+            mLocationRequest = locationRequest;
+        }
+
+        @Override
+        public String getLogString() {
+            if (mRegistered) {
+                return mProvider + " provider " + "+registration " + mIdentity + " -> "
+                        + mLocationRequest;
+            } else {
+                return mProvider + " provider " + "-registration " + mIdentity;
+            }
+        }
+    }
+
+    private static final class ProviderUpdateEvent extends ProviderEvent {
+
+        private final ProviderRequest mRequest;
+
+        private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
+            super(timeDelta, provider);
+            mRequest = request;
+        }
+
+        @Override
+        public String getLogString() {
+            return mProvider + " provider request = " + mRequest;
+        }
+    }
+
+    private static final class ProviderReceiveLocationEvent extends ProviderEvent {
+
+        private final int mNumLocations;
+
+        private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
+            super(timeDelta, provider);
+            mNumLocations = numLocations;
+        }
+
+        @Override
+        public String getLogString() {
+            return mProvider + " provider received location[" + mNumLocations + "]";
+        }
+    }
+
+    private static final class ProviderDeliverLocationEvent extends ProviderEvent {
+
+        private final int mNumLocations;
+        @Nullable private final CallerIdentity mIdentity;
+
+        private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
+                @Nullable CallerIdentity identity) {
+            super(timeDelta, provider);
+            mNumLocations = numLocations;
+            mIdentity = identity;
+        }
+
+        @Override
+        public String getLogString() {
+            return mProvider + " provider delivered location[" + mNumLocations + "] to "
+                    + mIdentity;
+        }
+    }
+
+    private static final class LocationPowerSaveModeEvent extends LogEvent {
+
+        @LocationPowerSaveMode
+        private final int mLocationPowerSaveMode;
+
+        private LocationPowerSaveModeEvent(long timeDelta,
+                @LocationPowerSaveMode int locationPowerSaveMode) {
+            super(timeDelta);
+            mLocationPowerSaveMode = locationPowerSaveMode;
+        }
+
+        @Override
+        public String getLogString() {
+            String mode;
+            switch (mLocationPowerSaveMode) {
+                case LOCATION_MODE_NO_CHANGE:
+                    mode = "NO_CHANGE";
+                    break;
+                case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+                    mode = "GPS_DISABLED_WHEN_SCREEN_OFF";
+                    break;
+                case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
+                    mode = "ALL_DISABLED_WHEN_SCREEN_OFF";
+                    break;
+                case LOCATION_MODE_FOREGROUND_ONLY:
+                    mode = "FOREGROUND_ONLY";
+                    break;
+                case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
+                    mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
+                    break;
+                default:
+                    mode = "UNKNOWN";
+                    break;
+            }
+            return "location power save mode changed to " + mode;
+        }
+    }
+
+    private static final class LocationEnabledEvent extends LogEvent {
+
+        private final int mUserId;
+        private final boolean mEnabled;
+
+        private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
+            super(timeDelta);
+            mUserId = userId;
+            mEnabled = enabled;
+        }
+
+        @Override
+        public String getLogString() {
+            return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
+        }
+    }
+
+    /**
+     * Aggregate statistics for a single package under a single provider.
+     */
+    public static final class AggregateStats {
+
+        @GuardedBy("this")
+        private int mAddedRequestCount;
+        @GuardedBy("this")
+        private int mActiveRequestCount;
+        @GuardedBy("this")
+        private int mForegroundRequestCount;
+
+        @GuardedBy("this")
+        private long mFastestIntervalMs = Long.MAX_VALUE;
+        @GuardedBy("this")
+        private long mSlowestIntervalMs = 0;
+
+        @GuardedBy("this")
+        private long mAddedTimeTotalMs;
+        @GuardedBy("this")
+        private long mAddedTimeLastUpdateRealtimeMs;
+
+        @GuardedBy("this")
+        private long mActiveTimeTotalMs;
+        @GuardedBy("this")
+        private long mActiveTimeLastUpdateRealtimeMs;
+
+        @GuardedBy("this")
+        private long mForegroundTimeTotalMs;
+        @GuardedBy("this")
+        private long mForegroundTimeLastUpdateRealtimeMs;
+
+        AggregateStats() {}
+
+        synchronized void markRequestAdded(long intervalMillis) {
+            if (mAddedRequestCount++ == 0) {
+                mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+            }
+
+            mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
+            mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
+        }
+
+        synchronized void markRequestRemoved() {
+            updateTotals();
+            --mAddedRequestCount;
+            Preconditions.checkState(mAddedRequestCount >= 0);
+
+            mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount);
+            mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount);
+        }
+
+        synchronized void markRequestActive() {
+            Preconditions.checkState(mAddedRequestCount > 0);
+            if (mActiveRequestCount++ == 0) {
+                mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+            }
+        }
+
+        synchronized void markRequestInactive() {
+            updateTotals();
+            --mActiveRequestCount;
+            Preconditions.checkState(mActiveRequestCount >= 0);
+        }
+
+        synchronized void markRequestForeground() {
+            Preconditions.checkState(mAddedRequestCount > 0);
+            if (mForegroundRequestCount++ == 0) {
+                mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+            }
+        }
+
+        synchronized void markRequestBackground() {
+            updateTotals();
+            --mForegroundRequestCount;
+            Preconditions.checkState(mForegroundRequestCount >= 0);
+        }
+
+        public synchronized void updateTotals() {
+            if (mAddedRequestCount > 0) {
+                long realtimeMs = SystemClock.elapsedRealtime();
+                mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
+                mAddedTimeLastUpdateRealtimeMs = realtimeMs;
+            }
+            if (mActiveRequestCount > 0) {
+                long realtimeMs = SystemClock.elapsedRealtime();
+                mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs;
+                mActiveTimeLastUpdateRealtimeMs = realtimeMs;
+            }
+            if (mForegroundRequestCount > 0) {
+                long realtimeMs = SystemClock.elapsedRealtime();
+                mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs;
+                mForegroundTimeLastUpdateRealtimeMs = realtimeMs;
+            }
+        }
+
+        @Override
+        public synchronized String toString() {
+            return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/"
+                    + intervalToString(mSlowestIntervalMs)
+                    + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs)
+                    + "/" + formatDuration(mActiveTimeTotalMs) + "/"
+                    + formatDuration(mForegroundTimeTotalMs);
+        }
+
+        private static String intervalToString(long intervalMs) {
+            if (intervalMs == LocationRequest.PASSIVE_INTERVAL) {
+                return "passive";
+            } else {
+                return MILLISECONDS.toSeconds(intervalMs) + "s";
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index 03938b2..0e157c2 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -56,7 +56,4 @@
 
     /** Returns a LocationUsageLogger. */
     LocationUsageLogger getLocationUsageLogger();
-
-    /** Returns a LocationEventLog. */
-    LocationEventLog getLocationEventLog();
 }
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
deleted file mode 100644
index b8b54b3..0000000
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location.injector;
-
-import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
-import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
-import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
-import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
-import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
-
-import static com.android.server.location.LocationManagerService.D;
-
-import android.annotation.Nullable;
-import android.location.LocationRequest;
-import android.location.provider.ProviderRequest;
-import android.location.util.identity.CallerIdentity;
-import android.os.Build;
-import android.os.PowerManager.LocationPowerSaveMode;
-
-import com.android.server.utils.eventlog.LocalEventLog;
-
-/** In memory event log for location events. */
-public class LocationEventLog extends LocalEventLog {
-
-    private static int getLogSize() {
-        if (Build.IS_DEBUGGABLE || D) {
-            return 500;
-        } else {
-            return 200;
-        }
-    }
-
-    private static final int EVENT_LOCATION_ENABLED = 1;
-    private static final int EVENT_PROVIDER_ENABLED = 2;
-    private static final int EVENT_PROVIDER_MOCKED = 3;
-    private static final int EVENT_PROVIDER_REGISTER_CLIENT = 4;
-    private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 5;
-    private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
-    private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
-    private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
-    private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
-
-    public LocationEventLog() {
-        super(getLogSize());
-    }
-
-    /** Logs a location enabled/disabled event. */
-    public void logLocationEnabled(int userId, boolean enabled) {
-        addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
-    }
-
-    /** Logs a location provider enabled/disabled event. */
-    public void logProviderEnabled(String provider, int userId, boolean enabled) {
-        addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
-    }
-
-    /** Logs a location provider being replaced/unreplaced by a mock provider. */
-    public void logProviderMocked(String provider, boolean mocked) {
-        addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
-    }
-
-    /** Logs a new client registration for a location provider. */
-    public void logProviderClientRegistered(String provider, CallerIdentity identity,
-            LocationRequest request) {
-        addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
-    }
-
-    /** Logs a client unregistration for a location provider. */
-    public void logProviderClientUnregistered(String provider,
-            CallerIdentity identity) {
-        addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
-    }
-
-    /** Logs a change to the provider request for a location provider. */
-    public void logProviderUpdateRequest(String provider, ProviderRequest request) {
-        addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
-    }
-
-    /** Logs a new incoming location for a location provider. */
-    public void logProviderReceivedLocations(String provider, int numLocations) {
-        if (Build.IS_DEBUGGABLE || D) {
-            addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
-        }
-    }
-
-    /** Logs a location deliver for a client of a location provider. */
-    public void logProviderDeliveredLocations(String provider, int numLocations,
-            CallerIdentity identity) {
-        if (Build.IS_DEBUGGABLE || D) {
-            addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
-        }
-    }
-
-    /** Logs that the location power save mode has changed. */
-    public void logLocationPowerSaveMode(
-            @LocationPowerSaveMode int locationPowerSaveMode) {
-        addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
-    }
-
-    @Override
-    protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
-        switch (event) {
-            case EVENT_LOCATION_ENABLED:
-                return new LocationEnabledEvent(timeDelta, (Integer) args[1], (Boolean) args[2]);
-            case EVENT_PROVIDER_ENABLED:
-                return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
-                        (Boolean) args[2]);
-            case EVENT_PROVIDER_MOCKED:
-                return new ProviderMockedEvent(timeDelta, (String) args[0], (Boolean) args[1]);
-            case EVENT_PROVIDER_REGISTER_CLIENT:
-                return new ProviderRegisterEvent(timeDelta, (String) args[0], true,
-                        (CallerIdentity) args[1], (LocationRequest) args[2]);
-            case EVENT_PROVIDER_UNREGISTER_CLIENT:
-                return new ProviderRegisterEvent(timeDelta, (String) args[0], false,
-                        (CallerIdentity) args[1], null);
-            case EVENT_PROVIDER_UPDATE_REQUEST:
-                return new ProviderUpdateEvent(timeDelta, (String) args[0],
-                        (ProviderRequest) args[1]);
-            case EVENT_PROVIDER_RECEIVE_LOCATION:
-                return new ProviderReceiveLocationEvent(timeDelta, (String) args[0],
-                        (Integer) args[1]);
-            case EVENT_PROVIDER_DELIVER_LOCATION:
-                return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
-                        (Integer) args[1], (CallerIdentity) args[2]);
-            case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
-                return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
-            default:
-                throw new AssertionError();
-        }
-    }
-
-    private static class ProviderEnabledEvent extends LogEvent {
-
-        private final String mProvider;
-        private final int mUserId;
-        private final boolean mEnabled;
-
-        protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
-                boolean enabled) {
-            super(timeDelta);
-            mProvider = provider;
-            mUserId = userId;
-            mEnabled = enabled;
-        }
-
-        @Override
-        public String getLogString() {
-            return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
-                    : "disabled");
-        }
-    }
-
-    private static class ProviderMockedEvent extends LogEvent {
-
-        private final String mProvider;
-        private final boolean mMocked;
-
-        protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
-            super(timeDelta);
-            mProvider = provider;
-            mMocked = mocked;
-        }
-
-        @Override
-        public String getLogString() {
-            if (mMocked) {
-                return mProvider + " provider added mock provider override";
-            } else {
-                return mProvider + " provider removed mock provider override";
-            }
-        }
-    }
-
-    private static class ProviderRegisterEvent extends LogEvent {
-
-        private final String mProvider;
-        private final boolean mRegistered;
-        private final CallerIdentity mIdentity;
-        @Nullable private final LocationRequest mLocationRequest;
-
-        private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
-                CallerIdentity identity, @Nullable LocationRequest locationRequest) {
-            super(timeDelta);
-            mProvider = provider;
-            mRegistered = registered;
-            mIdentity = identity;
-            mLocationRequest = locationRequest;
-        }
-
-        @Override
-        public String getLogString() {
-            if (mRegistered) {
-                return mProvider + " provider " + "+registration " + mIdentity + " -> "
-                        + mLocationRequest;
-            } else {
-                return mProvider + " provider " + "-registration " + mIdentity;
-            }
-        }
-    }
-
-    private static class ProviderUpdateEvent extends LogEvent {
-
-        private final String mProvider;
-        private final ProviderRequest mRequest;
-
-        private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
-            super(timeDelta);
-            mProvider = provider;
-            mRequest = request;
-        }
-
-        @Override
-        public String getLogString() {
-            return mProvider + " provider request = " + mRequest;
-        }
-    }
-
-    private static class ProviderReceiveLocationEvent extends LogEvent {
-
-        private final String mProvider;
-        private final int mNumLocations;
-
-        private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
-            super(timeDelta);
-            mProvider = provider;
-            mNumLocations = numLocations;
-        }
-
-        @Override
-        public String getLogString() {
-            return mProvider + " provider received location[" + mNumLocations + "]";
-        }
-    }
-
-    private static class ProviderDeliverLocationEvent extends LogEvent {
-
-        private final String mProvider;
-        private final int mNumLocations;
-        @Nullable private final CallerIdentity mIdentity;
-
-        private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
-                @Nullable CallerIdentity identity) {
-            super(timeDelta);
-            mProvider = provider;
-            mNumLocations = numLocations;
-            mIdentity = identity;
-        }
-
-        @Override
-        public String getLogString() {
-            return mProvider + " provider delivered location[" + mNumLocations + "] to "
-                    + mIdentity;
-        }
-    }
-
-    private static class LocationPowerSaveModeEvent extends LogEvent {
-
-        @LocationPowerSaveMode
-        private final int mLocationPowerSaveMode;
-
-        private LocationPowerSaveModeEvent(long timeDelta,
-                @LocationPowerSaveMode int locationPowerSaveMode) {
-            super(timeDelta);
-            mLocationPowerSaveMode = locationPowerSaveMode;
-        }
-
-        @Override
-        public String getLogString() {
-            String mode;
-            switch (mLocationPowerSaveMode) {
-                case LOCATION_MODE_NO_CHANGE:
-                    mode = "NO_CHANGE";
-                    break;
-                case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
-                    mode = "GPS_DISABLED_WHEN_SCREEN_OFF";
-                    break;
-                case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
-                    mode = "ALL_DISABLED_WHEN_SCREEN_OFF";
-                    break;
-                case LOCATION_MODE_FOREGROUND_ONLY:
-                    mode = "FOREGROUND_ONLY";
-                    break;
-                case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
-                    mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
-                    break;
-                default:
-                    mode = "UNKNOWN";
-                    break;
-            }
-            return "location power save mode changed to " + mode;
-        }
-    }
-
-    private static class LocationEnabledEvent extends LogEvent {
-
-        private final int mUserId;
-        private final boolean mEnabled;
-
-        private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
-            super(timeDelta);
-            mUserId = userId;
-            mEnabled = enabled;
-        }
-
-        @Override
-        public String getLogString() {
-            return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
index 532826a..cc00d56 100644
--- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
@@ -24,6 +24,8 @@
 import android.os.PowerManager.LocationPowerSaveMode;
 import android.util.Log;
 
+import com.android.server.location.eventlog.LocationEventLog;
+
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
index 1b74865..c47a64d 100644
--- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
@@ -25,6 +25,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
 
 import java.util.Objects;
 import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 221d4fb..388b5a4 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,6 +55,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -88,6 +89,7 @@
 import com.android.server.LocalServices;
 import com.android.server.location.LocationPermissions;
 import com.android.server.location.LocationPermissions.PermissionLevel;
+import com.android.server.location.eventlog.LocationEventLog;
 import com.android.server.location.fudger.LocationFudger;
 import com.android.server.location.injector.AlarmHelper;
 import com.android.server.location.injector.AppForegroundHelper;
@@ -95,7 +97,6 @@
 import com.android.server.location.injector.AppOpsHelper;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
 import com.android.server.location.injector.LocationPermissionsHelper;
 import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
 import com.android.server.location.injector.LocationPowerSaveModeHelper;
@@ -117,6 +118,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
 
 /**
@@ -321,7 +323,7 @@
                         + getRequest());
             }
 
-            mLocationEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
+            mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
 
             // initialization order is important as there are ordering dependencies
             mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -331,6 +333,10 @@
             mIsUsingHighPower = isUsingHighPower();
 
             onProviderListenerRegister();
+
+            if (mForeground) {
+                mEventLog.logProviderClientForeground(mName, getIdentity());
+            }
         }
 
         @GuardedBy("mLock")
@@ -342,7 +348,7 @@
 
             onProviderListenerUnregister();
 
-            mLocationEventLog.logProviderClientUnregistered(mName, getIdentity());
+            mEventLog.logProviderClientUnregistered(mName, getIdentity());
 
             if (D) {
                 Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -367,6 +373,8 @@
                 Preconditions.checkState(Thread.holdsLock(mLock));
             }
 
+            mEventLog.logProviderClientActive(mName, getIdentity());
+
             if (!getRequest().isHiddenFromAppOps()) {
                 mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
             }
@@ -387,6 +395,8 @@
             }
 
             onProviderListenerInactive();
+
+            mEventLog.logProviderClientInactive(mName, getIdentity());
         }
 
         /**
@@ -522,6 +532,12 @@
 
                 mForeground = foreground;
 
+                if (mForeground) {
+                    mEventLog.logProviderClientForeground(mName, getIdentity());
+                } else {
+                    mEventLog.logProviderClientBackground(mName, getIdentity());
+                }
+
                 // note that onProviderLocationRequestChanged() is always called
                 return onProviderLocationRequestChanged()
                         || mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
@@ -853,7 +869,7 @@
 
                     listener.deliverOnLocationChanged(deliverLocationResult,
                             mUseWakeLock ? mWakeLock::release : null);
-                    mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+                    mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
                             getIdentity());
                 }
 
@@ -1152,7 +1168,7 @@
 
                     // we currently don't hold a wakelock for getCurrentLocation deliveries
                     listener.deliverOnLocationChanged(deliverLocationResult, null);
-                    mLocationEventLog.logProviderDeliveredLocations(mName,
+                    mEventLog.logProviderDeliveredLocations(mName,
                             locationResult != null ? locationResult.size() : 0, getIdentity());
                 }
 
@@ -1219,6 +1235,9 @@
     @GuardedBy("mLock")
     private final ArrayList<ProviderEnabledListener> mEnabledListeners;
 
+    private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+
+    protected final LocationEventLog mEventLog;
     protected final LocationManagerInternal mLocationManagerInternal;
     protected final SettingsHelper mSettingsHelper;
     protected final UserInfoHelper mUserHelper;
@@ -1231,7 +1250,6 @@
     protected final LocationAttributionHelper mLocationAttributionHelper;
     protected final LocationUsageLogger mLocationUsageLogger;
     protected final LocationFudger mLocationFudger;
-    protected final LocationEventLog mLocationEventLog;
 
     private final UserListener mUserChangedListener = this::onUserChanged;
     private final UserSettingChangedListener mLocationEnabledChangedListener =
@@ -1269,8 +1287,8 @@
     @GuardedBy("mLock")
     private @Nullable OnAlarmListener mDelayedRegister;
 
-    public LocationProviderManager(Context context, Injector injector, String name,
-            @Nullable PassiveLocationProviderManager passiveManager) {
+    public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
+            String name, @Nullable PassiveLocationProviderManager passiveManager) {
         mContext = context;
         mName = Objects.requireNonNull(name);
         mPassiveManager = passiveManager;
@@ -1279,7 +1297,9 @@
         mLastLocations = new SparseArray<>(2);
 
         mEnabledListeners = new ArrayList<>();
+        mProviderRequestListeners = new CopyOnWriteArrayList<>();
 
+        mEventLog = eventLog;
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
         mSettingsHelper = injector.getSettingsHelper();
@@ -1292,7 +1312,6 @@
         mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
         mLocationAttributionHelper = injector.getLocationAttributionHelper();
         mLocationUsageLogger = injector.getLocationUsageLogger();
-        mLocationEventLog = injector.getLocationEventLog();
         mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
 
         mProvider = new MockableLocationProvider(mLock);
@@ -1344,6 +1363,7 @@
             // if external entities are registering listeners it's their responsibility to
             // unregister them before stopManager() is called
             Preconditions.checkState(mEnabledListeners.isEmpty());
+            mProviderRequestListeners.clear();
 
             mEnabled.clear();
             mLastLocations.clear();
@@ -1404,6 +1424,16 @@
         }
     }
 
+    /** Add a {@link IProviderRequestListener}. */
+    public void addProviderRequestListener(IProviderRequestListener listener) {
+        mProviderRequestListeners.add(listener);
+    }
+
+    /** Remove a {@link IProviderRequestListener}. */
+    public void removeProviderRequestListener(IProviderRequestListener listener) {
+        mProviderRequestListeners.remove(listener);
+    }
+
     public void setRealProvider(@Nullable AbstractLocationProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mState != STATE_STOPPED);
@@ -1421,7 +1451,7 @@
         synchronized (mLock) {
             Preconditions.checkState(mState != STATE_STOPPED);
 
-            mLocationEventLog.logProviderMocked(mName, provider != null);
+            mEventLog.logProviderMocked(mName, provider != null);
 
             final long identity = Binder.clearCallingIdentity();
             try {
@@ -1873,8 +1903,7 @@
         Preconditions.checkState(delayMs >= 0 && delayMs <= newRequest.getIntervalMillis());
 
         if (delayMs < MIN_REQUEST_DELAY_MS) {
-            mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
-            mProvider.getController().setRequest(newRequest);
+            setProviderRequest(newRequest);
         } else {
             if (D) {
                 Log.d(TAG, mName + " provider delaying request update " + newRequest + " by "
@@ -1886,8 +1915,7 @@
                 public void onAlarm() {
                     synchronized (mLock) {
                         if (mDelayedRegister == this) {
-                            mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
-                            mProvider.getController().setRequest(newRequest);
+                            setProviderRequest(newRequest);
                             mDelayedRegister = null;
                         }
                     }
@@ -1906,8 +1934,23 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
-        mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
+        setProviderRequest(ProviderRequest.EMPTY_REQUEST);
+    }
+
+    @GuardedBy("mLock")
+    private void setProviderRequest(ProviderRequest request) {
+        mEventLog.logProviderUpdateRequest(mName, request);
+        mProvider.getController().setRequest(request);
+
+        FgThread.getHandler().post(() -> {
+            for (IProviderRequestListener listener : mProviderRequestListeners) {
+                try {
+                    listener.onProviderRequestChanged(mName, request);
+                } catch (RemoteException e) {
+                    mProviderRequestListeners.remove(listener);
+                }
+            }
+        });
     }
 
     @GuardedBy("mLock")
@@ -2232,7 +2275,7 @@
             }
 
             // don't log location received for passive provider because it's spammy
-            mLocationEventLog.logProviderReceivedLocations(mName, filtered.size());
+            mEventLog.logProviderReceivedLocations(mName, filtered.size());
         } else {
             // passive provider should get already filtered results as input
             filtered = locationResult;
@@ -2332,7 +2375,7 @@
             if (D) {
                 Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
             }
-            mLocationEventLog.logProviderEnabled(mName, userId, enabled);
+            mEventLog.logProviderEnabled(mName, userId, enabled);
         }
 
         // clear last locations if we become disabled
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index b35af4f..027f4e9 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.location.eventlog.LocationEventLog;
 import com.android.server.location.injector.Injector;
 
 import java.util.Collection;
@@ -33,8 +34,9 @@
  */
 public class PassiveLocationProviderManager extends LocationProviderManager {
 
-    public PassiveLocationProviderManager(Context context, Injector injector) {
-        super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
+    public PassiveLocationProviderManager(Context context, Injector injector,
+            LocationEventLog eventLog) {
+        super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index e2e5046..87e170a 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.app.BroadcastOptions;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -58,6 +59,16 @@
     private static final String TAG = "PendingIntentHolder";
     private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
     private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+    // Filter apps regardless of the phone's locked/unlocked state.
+    private static final int PACKAGE_MANAGER_COMMON_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+    /**
+     * Denotes the duration during which a media button receiver will be exempted from
+     * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
+     * background state while it receives a media key event.
+     */
+    private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
 
     private final int mUserId;
     private final PendingIntent mPendingIntent;
@@ -105,40 +116,22 @@
      * @return Can be {@code null} if pending intent was null.
      */
     public static MediaButtonReceiverHolder create(Context context, int userId,
-            PendingIntent pendingIntent) {
+            PendingIntent pendingIntent, String sessionPackageName) {
         if (pendingIntent == null) {
             return null;
         }
-        ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
-                ? pendingIntent.getIntent().getComponent() : null;
+        int componentType = getComponentType(pendingIntent);
+        ComponentName componentName = getComponentName(pendingIntent, componentType);
         if (componentName != null) {
-            // Explicit intent, where component name is in the PendingIntent.
             return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
-                    getComponentType(context, componentName));
-        }
-
-        // Implicit intent, where component name isn't in the PendingIntent. Try resolve.
-        PackageManager pm = context.getPackageManager();
-        Intent intent = pendingIntent.getIntent();
-        if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
-        } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
-                != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
-        } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
+                    componentType);
         }
 
         // Failed to resolve target component for the pending intent. It's unlikely to be usable.
-        // However, the pending intent would be still used, just to follow the legacy behavior.
+        // However, the pending intent would be still used, so setting the package name to the
+        // package name of the session that set this pending intent.
         Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
-        String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
-                ? pendingIntent.getIntent().getPackage() : null;
-        return new MediaButtonReceiverHolder(userId, pendingIntent,
-                packageName != null ? packageName : "");
+        return new MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName);
     }
 
     public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
@@ -201,6 +194,9 @@
         // TODO: Find a way to also send PID/UID in secure way.
         mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
 
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setTemporaryAppWhitelistDuration(
+                FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
         if (mPendingIntent != null) {
             if (DEBUG_KEY_EVENT) {
                 Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
@@ -208,7 +204,8 @@
             }
             try {
                 mPendingIntent.send(
-                        context, resultCode, mediaButtonIntent, onFinishedListener, handler);
+                        context, resultCode, mediaButtonIntent, onFinishedListener, handler,
+                        /* requiredPermission= */ null, options.toBundle());
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
                 return false;
@@ -231,7 +228,8 @@
                         break;
                     default:
                         // Legacy behavior for other cases.
-                        context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                        context.sendBroadcastAsUser(mediaButtonIntent, userHandle,
+                                /* receiverPermission= */ null, options.toBundle());
                 }
             } catch (Exception e) {
                 Log.w(TAG, "Error sending media button to the restored intent "
@@ -269,6 +267,18 @@
                 String.valueOf(mComponentType));
     }
 
+    @ComponentType
+    private static int getComponentType(PendingIntent pendingIntent) {
+        if (pendingIntent.isBroadcast()) {
+            return COMPONENT_TYPE_BROADCAST;
+        } else if (pendingIntent.isActivity()) {
+            return COMPONENT_TYPE_ACTIVITY;
+        } else if (pendingIntent.isForegroundService() || pendingIntent.isService()) {
+            return COMPONENT_TYPE_SERVICE;
+        }
+        return COMPONENT_TYPE_INVALID;
+    }
+
     /**
      * Gets the type of the component
      *
@@ -284,9 +294,7 @@
         PackageManager pm = context.getPackageManager();
         try {
             ActivityInfo activityInfo = pm.getActivityInfo(componentName,
-                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.GET_ACTIVITIES);
+                    PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_ACTIVITIES);
             if (activityInfo != null) {
                 return COMPONENT_TYPE_ACTIVITY;
             }
@@ -294,9 +302,7 @@
         }
         try {
             ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
-                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.GET_SERVICES);
+                    PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
             if (serviceInfo != null) {
                 return COMPONENT_TYPE_SERVICE;
             }
@@ -306,40 +312,29 @@
         return COMPONENT_TYPE_BROADCAST;
     }
 
-    private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     filter apps regardless of the phone's locked/unlocked state.
-        // - GET_SERVICES: Return service
-        return createComponentName(pm.resolveService(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                        | PackageManager.GET_SERVICES));
-    }
-
-    private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
-            PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     filter apps regardless of the phone's locked/unlocked state.
-        List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-        return (resolveInfos != null && !resolveInfos.isEmpty())
-                ? createComponentName(resolveInfos.get(0)) : null;
-    }
-
-    private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     Filter apps regardless of the phone's locked/unlocked state.
-        // - MATCH_DEFAULT_ONLY:
-        //     Implicit intent receiver should be set as default. Only needed for activity.
-        // - GET_ACTIVITIES: Return activity
-        return createComponentName(pm.resolveActivity(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                        | PackageManager.MATCH_DEFAULT_ONLY
-                        | PackageManager.GET_ACTIVITIES));
+    private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) {
+        List<ResolveInfo> resolveInfos = null;
+        switch (componentType) {
+            case COMPONENT_TYPE_ACTIVITY:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS
+                                | PackageManager.MATCH_DEFAULT_ONLY /* Implicit intent receiver
+                                should be set as default. Only needed for activity. */
+                                | PackageManager.GET_ACTIVITIES);
+                break;
+            case COMPONENT_TYPE_SERVICE:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
+                break;
+            case COMPONENT_TYPE_BROADCAST:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS);
+                break;
+        }
+        if (resolveInfos != null && !resolveInfos.isEmpty()) {
+            return createComponentName(resolveInfos.get(0));
+        }
+        return null;
     }
 
     private static ComponentName createComponentName(ResolveInfo resolveInfo) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 63618ee..7ef4924 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -100,9 +100,12 @@
     /**
      * Implement this to customize the logic for which MediaButtonReceiver should consume a
      * dispatched key event.
-     *
-     * Note: This pending intent will have lower priority over the {@link MediaSession.Token}
+     * <p>
+     * This pending intent will have lower priority over the {@link MediaSession.Token}
      * returned from {@link #getMediaSession(KeyEvent, int, boolean)}.
+     * <p>
+     * Use a pending intent with an explicit intent; setting a pending intent with an implicit
+     * intent that cannot be resolved to a certain component name will fail.
      *
      * @return a {@link PendingIntent} instance that should receive the dispatched key event.
      */
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ae58d4c..74111be 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -843,7 +843,8 @@
         }
 
         @Override
-        public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+        public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
+                throws RemoteException {
             final long token = Binder.clearCallingIdentity();
             try {
                 if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -851,7 +852,7 @@
                     return;
                 }
                 mMediaButtonReceiverHolder =
-                        MediaButtonReceiverHolder.create(mContext, mUserId, pi);
+                        MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
                 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c0381e4..6c1d399 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2124,7 +2124,8 @@
                             uid, asSystemService);
                     if (pi != null) {
                         mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext,
-                                mCurrentFullUserRecord.mFullUserId, pi);
+                                mCurrentFullUserRecord.mFullUserId, pi,
+                                /* sessionPackageName= */ "");
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b99a552..aa7da54 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1961,14 +1961,13 @@
             if (state.network != null) {
                 mNetIdToSubId.put(state.network.netId, parseSubId(state));
             }
-            if (state.networkInfo != null && state.networkInfo.isConnected()) {
-                // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
-                // in the object created here is never used and its value doesn't matter, so use
-                // NETWORK_TYPE_UNKNOWN.
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
-                identified.put(state, ident);
-            }
+
+            // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+            // in the object created here is never used and its value doesn't matter, so use
+            // NETWORK_TYPE_UNKNOWN.
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+            identified.put(state, ident);
         }
 
         final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2043,8 +2042,7 @@
         // One final pass to catch any metered ifaces that don't have explicitly
         // defined policies; typically Wi-Fi networks.
         for (NetworkState state : states) {
-            if (state.networkInfo != null && state.networkInfo.isConnected()
-                    && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
                 matchingIfaces.clear();
                 collectIfaces(matchingIfaces, state);
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0ab35a9..9706bce 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
 import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkStats;
@@ -1264,7 +1263,7 @@
 
     /**
      * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
-     * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+     * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
      * they are combined under a single {@link NetworkIdentitySet}.
      */
     @GuardedBy("mStatsLock")
@@ -1294,84 +1293,82 @@
         final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
         final ArraySet<String> mobileIfaces = new ArraySet<>();
         for (NetworkState state : states) {
-            if (state.networkInfo.isConnected()) {
-                final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
-                final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
-                final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
-                        : getSubTypeForState(state);
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        isDefault, subType);
+            final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
+            final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+            final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+                    : getSubTypeForState(state);
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    isDefault, subType);
 
-                // Traffic occurring on the base interface is always counted for
-                // both total usage and UID details.
-                final String baseIface = state.linkProperties.getInterfaceName();
-                if (baseIface != null) {
-                    findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+            // Traffic occurring on the base interface is always counted for
+            // both total usage and UID details.
+            final String baseIface = state.linkProperties.getInterfaceName();
+            if (baseIface != null) {
+                findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+                findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
 
-                    // Build a separate virtual interface for VT (Video Telephony) data usage.
-                    // Only do this when IMS is not metered, but VT is metered.
-                    // If IMS is metered, then the IMS network usage has already included VT usage.
-                    // VT is considered always metered in framework's layer. If VT is not metered
-                    // per carrier's policy, modem will report 0 usage for VT calls.
-                    if (state.networkCapabilities.hasCapability(
-                            NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+                // Build a separate virtual interface for VT (Video Telephony) data usage.
+                // Only do this when IMS is not metered, but VT is metered.
+                // If IMS is metered, then the IMS network usage has already included VT usage.
+                // VT is considered always metered in framework's layer. If VT is not metered
+                // per carrier's policy, modem will report 0 usage for VT calls.
+                if (state.networkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
 
-                        // Copy the identify from IMS one but mark it as metered.
-                        NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
-                                ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
-                                ident.getRoaming(), true /* metered */,
-                                true /* onDefaultNetwork */);
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
-                    }
-
-                    if (isMobile) {
-                        mobileIfaces.add(baseIface);
-                    }
+                    // Copy the identify from IMS one but mark it as metered.
+                    NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+                            ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+                            ident.getRoaming(), true /* metered */,
+                            true /* onDefaultNetwork */);
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
                 }
 
-                // Traffic occurring on stacked interfaces is usually clatd.
-                //
-                // UID stats are always counted on the stacked interface and never on the base
-                // interface, because the packets on the base interface do not actually match
-                // application sockets (they're not IPv4) and thus the app uid is not known.
-                // For receive this is obvious: packets must be translated from IPv6 to IPv4
-                // before the application socket can be found.
-                // For transmit: either they go through the clat daemon which by virtue of going
-                // through userspace strips the original socket association during the IPv4 to
-                // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
-                // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
-                // which don't trigger again post ebpf translation.
-                // (as such stats accounted to the clat uid are ignored)
-                //
-                // Interface stats are more complicated.
-                //
-                // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
-                // *all* statistics are collected by iptables on the stacked v4-* interface.
-                //
-                // Additionally for ingress all packets bound for the clat IPv6 address are dropped
-                // in ip6tables raw prerouting and thus even non-offloaded packets are only
-                // accounted for on the stacked interface.
-                //
-                // For egress, packets subject to eBPF offload never appear on the base interface
-                // and only appear on the stacked interface. Thus to ensure packets increment
-                // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
-                // (or non eBPF offloaded) TX they would appear on both, however egress interface
-                // accounting is explicitly bypassed for traffic from the clat uid.
-                //
-                final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
-                for (LinkProperties stackedLink : stackedLinks) {
-                    final String stackedIface = stackedLink.getInterfaceName();
-                    if (stackedIface != null) {
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
-                        if (isMobile) {
-                            mobileIfaces.add(stackedIface);
-                        }
+                if (isMobile) {
+                    mobileIfaces.add(baseIface);
+                }
+            }
 
-                        mStatsFactory.noteStackedIface(stackedIface, baseIface);
+            // Traffic occurring on stacked interfaces is usually clatd.
+            //
+            // UID stats are always counted on the stacked interface and never on the base
+            // interface, because the packets on the base interface do not actually match
+            // application sockets (they're not IPv4) and thus the app uid is not known.
+            // For receive this is obvious: packets must be translated from IPv6 to IPv4
+            // before the application socket can be found.
+            // For transmit: either they go through the clat daemon which by virtue of going
+            // through userspace strips the original socket association during the IPv4 to
+            // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+            // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+            // which don't trigger again post ebpf translation.
+            // (as such stats accounted to the clat uid are ignored)
+            //
+            // Interface stats are more complicated.
+            //
+            // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+            // *all* statistics are collected by iptables on the stacked v4-* interface.
+            //
+            // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+            // in ip6tables raw prerouting and thus even non-offloaded packets are only
+            // accounted for on the stacked interface.
+            //
+            // For egress, packets subject to eBPF offload never appear on the base interface
+            // and only appear on the stacked interface. Thus to ensure packets increment
+            // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+            // (or non eBPF offloaded) TX they would appear on both, however egress interface
+            // accounting is explicitly bypassed for traffic from the clat uid.
+            //
+            final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+            for (LinkProperties stackedLink : stackedLinks) {
+                final String stackedIface = stackedLink.getInterfaceName();
+                if (stackedIface != null) {
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+                    if (isMobile) {
+                        mobileIfaces.add(stackedIface);
                     }
+
+                    mStatsFactory.noteStackedIface(stackedIface, baseIface);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6843733..4c3dfbf 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -378,7 +378,9 @@
     static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
             Adjustment.KEY_CONTEXTUAL_ACTIONS,
             Adjustment.KEY_TEXT_REPLIES,
-            Adjustment.KEY_NOT_CONVERSATION};
+            Adjustment.KEY_NOT_CONVERSATION,
+            Adjustment.KEY_IMPORTANCE,
+            Adjustment.KEY_RANKING_SCORE};
 
     static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
             RoleManager.ROLE_DIALER,
@@ -3041,7 +3043,8 @@
                         }
 
                         Binder windowToken = new Binder();
-                        mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
+                        mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId,
+                                null /* options */);
                         record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token,
                                 text, callback, duration, windowToken, displayId, textCallback);
                         mToastQueue.add(record);
@@ -9047,7 +9050,8 @@
     public class NotificationAssistants extends ManagedServices {
         static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
 
-        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments";
+        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES_OLD = "q_allowed_adjustments";
+        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "s_allowed_adjustments";
         private static final String ATT_TYPES = "types";
 
         private final Object mLock = new Object();
@@ -9149,13 +9153,19 @@
 
         @Override
         protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
-            if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
+            if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)
+                    || TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
                 final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
                 synchronized (mLock) {
                     mAllowedAdjustments.clear();
                     if (!TextUtils.isEmpty(types)) {
                         mAllowedAdjustments.addAll(Arrays.asList(types.split(",")));
                     }
+                    if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)) {
+                        if (DEBUG) Slog.d(TAG, "Migrate allowed adjustments.");
+                        mAllowedAdjustments.addAll(
+                                Arrays.asList(DEFAULT_ALLOWED_ADJUSTMENTS));
+                    }
                 }
             }
         }
@@ -10103,7 +10113,9 @@
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (info.isSystem || hasCompanionDevice(info)) {
+                    if (info.isSystem
+                            || hasCompanionDevice(info)
+                            || mAssistants.isServiceTokenValidLocked(info.service)) {
                         notifyNotificationChannelChanged(
                                 info, pkg, user, channel, modificationType);
                     }
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 50fb176..fd2fb1f 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -50,6 +50,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.res.ApkAssets;
 import android.net.Uri;
 import android.os.Binder;
@@ -1376,18 +1377,18 @@
                 targetPackageNames = pm.getTargetPackageNames(userId);
             }
 
-            final Map<String, List<String>> pendingChanges =
+            final Map<String, OverlayPaths> pendingChanges =
                     new ArrayMap<>(targetPackageNames.size());
             synchronized (mLock) {
-                final List<String> frameworkOverlays =
-                        mImpl.getEnabledOverlayPackageNames("android", userId);
+                final OverlayPaths frameworkOverlays =
+                        mImpl.getEnabledOverlayPaths("android", userId);
                 for (final String targetPackageName : targetPackageNames) {
-                    List<String> list = new ArrayList<>();
+                    final OverlayPaths.Builder list = new OverlayPaths.Builder();
                     if (!"android".equals(targetPackageName)) {
                         list.addAll(frameworkOverlays);
                     }
-                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
-                    pendingChanges.put(targetPackageName, list);
+                    list.addAll(mImpl.getEnabledOverlayPaths(targetPackageName, userId));
+                    pendingChanges.put(targetPackageName, list.build());
                 }
             }
 
@@ -1395,7 +1396,7 @@
             for (final String targetPackageName : targetPackageNames) {
                 if (DEBUG) {
                     Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
-                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
+                            + pendingChanges.get(targetPackageName)
                             + "] userId=" + userId);
                 }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index e60411bb..c547c36 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -31,6 +31,7 @@
 import android.content.om.OverlayInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -697,19 +698,20 @@
         removeIdmapIfPossible(oi);
     }
 
-    List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
+    OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName,
             final int userId) {
         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
-        final List<String> paths = new ArrayList<>(overlays.size());
+        final OverlayPaths.Builder paths = new OverlayPaths.Builder();
         final int n = overlays.size();
         for (int i = 0; i < n; i++) {
             final OverlayInfo oi = overlays.get(i);
-            if (oi.isEnabled()) {
-                paths.add(oi.packageName);
+            if (!oi.isEnabled()) {
+                continue;
             }
+            paths.addApkPath(oi.baseCodePath);
         }
-        return paths;
+        return paths.build();
     }
 
     /**
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+    private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+    private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final TombstoneWatcher mWatcher;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final SparseArray<TombstoneFile> mTombstones;
+
+    NativeTombstoneManager(Context context) {
+        mTombstones = new SparseArray<TombstoneFile>();
+        mContext = context;
+
+        final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+                THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+        thread.start();
+        mHandler = thread.getThreadHandler();
+
+        mWatcher = new TombstoneWatcher();
+        mWatcher.startWatching();
+    }
+
+    void onSystemReady() {
+        // Scan existing tombstones.
+        mHandler.post(() -> {
+            final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+            for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+                if (tombstoneFiles[i].isFile()) {
+                    handleTombstone(tombstoneFiles[i]);
+                }
+            }
+        });
+    }
+
+    private void handleTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.startsWith("tombstone_")) {
+            return;
+        }
+
+        if (filename.endsWith(".pb")) {
+            handleProtoTombstone(path);
+        } else {
+            BootReceiver.addTombstoneToDropBox(mContext, path);
+        }
+    }
+
+    private void handleProtoTombstone(File path) {
+        final String filename = path.getName();
+        if (!filename.endsWith(".pb")) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        final String suffix = filename.substring("tombstone_".length());
+        final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+        int number;
+        try {
+            number = Integer.parseInt(numberStr);
+            if (number < 0 || number > 99) {
+                Slog.w(TAG, "unexpected tombstone name: " + path);
+                return;
+            }
+        } catch (NumberFormatException ex) {
+            Slog.w(TAG, "unexpected tombstone name: " + path);
+            return;
+        }
+
+        ParcelFileDescriptor pfd;
+        try {
+            pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+        } catch (FileNotFoundException ex) {
+            Slog.w(TAG, "failed to open " + path, ex);
+            return;
+        }
+
+        final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+        if (!parsedTombstone.isPresent()) {
+            IoUtils.closeQuietly(pfd);
+            return;
+        }
+
+        synchronized (mLock) {
+            TombstoneFile previous = mTombstones.get(number);
+            if (previous != null) {
+                previous.dispose();
+            }
+
+            mTombstones.put(number, parsedTombstone.get());
+        }
+    }
+
+    static class TombstoneFile {
+        final ParcelFileDescriptor mPfd;
+
+        final @UserIdInt int mUserId;
+        final @AppIdInt int mAppId;
+
+        boolean mPurged = false;
+
+        TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+            mPfd = pfd;
+            mUserId = userId;
+            mAppId = appId;
+        }
+
+        public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+            if (mPurged) {
+                return false;
+            }
+
+            if (userId.isPresent() && userId.get() != mUserId) {
+                return false;
+            }
+
+            if (appId.isPresent() && appId.get() != mAppId) {
+                return false;
+            }
+
+            return true;
+        }
+
+        public void dispose() {
+            IoUtils.closeQuietly(mPfd);
+        }
+
+        static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+            final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+            final ProtoInputStream stream = new ProtoInputStream(is);
+
+            int uid = 0;
+            String selinuxLabel = "";
+
+            try {
+                while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                    switch (stream.getFieldNumber()) {
+                        case (int) Tombstone.UID:
+                            uid = stream.readInt(Tombstone.UID);
+                            break;
+
+                        case (int) Tombstone.SELINUX_LABEL:
+                            selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+                            break;
+
+                        default:
+                            break;
+                    }
+                }
+            } catch (IOException ex) {
+                Slog.e(TAG, "Failed to parse tombstone", ex);
+                return Optional.empty();
+            }
+
+            if (!UserHandle.isApp(uid)) {
+                Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+                return Optional.empty();
+            }
+
+            final int userId = UserHandle.getUserId(uid);
+            final int appId = UserHandle.getAppId(uid);
+
+            if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+                Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+                return Optional.empty();
+            }
+
+            return Optional.of(new TombstoneFile(pfd, userId, appId));
+        }
+    }
+
+    class TombstoneWatcher extends FileObserver {
+        TombstoneWatcher() {
+            // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+            // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+            // isn't supported (MOVED_TO).
+            super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+        }
+
+        @Override
+        public void onEvent(int event, @Nullable String path) {
+            mHandler.post(() -> {
+                handleTombstone(new File(TOMBSTONE_DIR, path));
+            });
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+    private static final String TAG = "NativeTombstoneManagerService";
+
+    private NativeTombstoneManager mManager;
+
+    public NativeTombstoneManagerService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mManager = new NativeTombstoneManager(getContext());
+        LocalServices.addService(NativeTombstoneManager.class, mManager);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            mManager.onSystemReady();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
     /**
      * Fetch or calculate checksums for the collection of files.
      *
-     * @param filesToChecksum       split name, null for base and File to fetch checksums for
-     * @param optional              mask to fetch readily available checksums
-     * @param required              mask to forcefully calculate if not available
-     * @param installerPackageName  package name of the installer of the packages
-     * @param trustedInstallers     array of certificate to trust, two specific cases:
-     *                              null - trust anybody,
-     *                              [] - trust nobody.
-     * @param statusReceiver        to receive the resulting checksums
+     * @param filesToChecksum          split name, null for base and File to fetch checksums for
+     * @param optional                 mask to fetch readily available checksums
+     * @param required                 mask to forcefully calculate if not available
+     * @param installerPackageName     package name of the installer of the packages
+     * @param trustedInstallers        array of certificate to trust, two specific cases:
+     *                                 null - trust anybody,
+     *                                 [] - trust nobody.
+     * @param onChecksumsReadyListener to receive the resulting checksums
      */
     public static void getChecksums(List<Pair<String, File>> filesToChecksum,
             @Checksum.Type int optional,
             @Checksum.Type int required,
             @Nullable String installerPackageName,
             @Nullable Certificate[] trustedInstallers,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector) {
         List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
         for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
         }
 
         long startTime = SystemClock.uptimeMillis();
-        processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
-                startTime);
+        processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+                injector, startTime);
     }
 
     private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
             List<Map<Integer, ApkChecksum>> result,
             @Checksum.Type int required,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector,
             long startTime) {
         final boolean timeout =
@@ -350,7 +349,7 @@
                         // Not ready, come back later.
                         injector.getHandler().postDelayed(() -> {
                             processRequiredChecksums(filesToChecksum, result, required,
-                                    statusReceiver, injector, startTime);
+                                    onChecksumsReadyListener, injector, startTime);
                         }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
                         return;
                     }
@@ -363,13 +362,9 @@
             }
         }
 
-        final Intent intent = new Intent();
-        intent.putExtra(EXTRA_CHECKSUMS,
-                allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
         try {
-            statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
-        } catch (IntentSender.SendIntentException e) {
+            onChecksumsReadyListener.onChecksumsReady(allChecksums);
+        } catch (RemoteException e) {
             Slog.w(TAG, e);
         }
     }
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
index a17967f..c18d0e9 100644
--- a/services/core/java/com/android/server/pm/DefaultAppProvider.java
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -41,14 +41,18 @@
 public class DefaultAppProvider {
     @NonNull
     private final Supplier<RoleManager> mRoleManagerSupplier;
+    @NonNull
+    private final Supplier<UserManagerInternal> mUserManagerInternalSupplier;
 
     /**
      * Create a new instance of this class
      *
      * @param roleManagerSupplier the supplier for {@link RoleManager}
      */
-    public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+    public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier,
+            @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) {
         mRoleManagerSupplier = roleManagerSupplier;
+        mUserManagerInternalSupplier = userManagerInternalSupplier;
     }
 
     /**
@@ -132,7 +136,8 @@
      */
     @Nullable
     public String getDefaultHome(@NonNull int userId) {
-        return getRoleHolder(RoleManager.ROLE_HOME, userId);
+        return getRoleHolder(RoleManager.ROLE_HOME,
+                mUserManagerInternalSupplier.get().getProfileParentId(userId));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * 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.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+    @IntDef({
+            Direction.TO_PARENT,
+            Direction.TO_PROFILE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Direction {
+        int TO_PARENT = 0;
+        int TO_PROFILE = 1;
+    }
+
+    /** The intent filter that's used */
+    public final IntentFilter filter;
+
+    /**
+     * The flags related to the forwarding, e.g.
+     * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+     * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+     */
+    public final int flags;
+
+    /**
+     * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+     * {@link Direction#TO_PROFILE}.
+     */
+    public final @Direction int direction;
+
+    /**
+     * Whether this cross profile intent filter would allow personal data to be shared into
+     * the work profile. If this is {@code true}, this intent filter should be only added to
+     * the profile if the admin does not enable
+     * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+     */
+    public final boolean letsPersonalDataIntoProfile;
+
+    private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+            @Direction int direction, boolean letsPersonalDataIntoProfile) {
+        this.filter = requireNonNull(filter);
+        this.flags = flags;
+        this.direction = direction;
+        this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+    }
+
+    static final class Builder {
+        private IntentFilter mFilter = new IntentFilter();
+        private int mFlags;
+        private @Direction int mDirection;
+        private boolean mLetsPersonalDataIntoProfile;
+
+        Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+            mDirection = direction;
+            mFlags = flags;
+            mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+        }
+
+        Builder addAction(String action) {
+            mFilter.addAction(action);
+            return this;
+        }
+
+        Builder addCategory(String category) {
+            mFilter.addCategory(category);
+            return this;
+        }
+
+        Builder addDataType(String type) {
+            try {
+                mFilter.addDataType(type);
+            } catch (IntentFilter.MalformedMimeTypeException e) {
+                // ignore
+            }
+            return this;
+        }
+
+        Builder addDataScheme(String scheme) {
+            mFilter.addDataScheme(scheme);
+            return this;
+        }
+
+        DefaultCrossProfileIntentFilter build() {
+            return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+                    mLetsPersonalDataIntoProfile);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+    private DefaultCrossProfileIntentFiltersUtils() {
+    }
+
+    // Intents from profile to parent user
+    /** Emergency call intent with mime type is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Emergency call intent with data schemes is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /**
+     * Dial intent with no data scheme or type can be handled by either managed profile or its
+     * parent user.
+     */
+    private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .build();
+
+    /** Pressing the call button can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_BUTTON)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** SMS and MMS are exclusively handled by the primary user. */
+    private static final DefaultCrossProfileIntentFilter SMS_MMS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addAction(Intent.ACTION_SENDTO)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("sms")
+                    .addDataScheme("smsto")
+                    .addDataScheme("mms")
+                    .addDataScheme("mmsto")
+                    .build();
+
+    /** Mobile network settings is always shown in the primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            MOBILE_NETWORK_SETTINGS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+                    .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** HOME intent is always resolved by the primary user. */
+    static final DefaultCrossProfileIntentFilter HOME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_HOME)
+                    .build();
+
+    /** Get content can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_GET_CONTENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Open document intent can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_OPEN_DOCUMENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick for any data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick without data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Speech recognition can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(ACTION_RECOGNIZE_SPEECH)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Media capture can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+                    .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+                    .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+                    .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Alarm setting can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter SET_ALARM =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(AlarmClock.ACTION_SET_ALARM)
+                    .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+                    .addAction(AlarmClock.ACTION_SET_TIMER)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    // Intents from parent to profile user
+
+    /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+    private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_SEND)
+                    .addAction(Intent.ACTION_SEND_MULTIPLE)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** USB devices attached can get forwarded to the profile. */
+    private static final DefaultCrossProfileIntentFilter
+            USB_DEVICE_ATTACHED =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+                    .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+        return Arrays.asList(
+                EMERGENCY_CALL_MIME,
+                EMERGENCY_CALL_DATA,
+                DIAL_MIME,
+                DIAL_DATA,
+                DIAL_RAW,
+                CALL_BUTTON,
+                SMS_MMS,
+                SET_ALARM,
+                MEDIA_CAPTURE,
+                RECOGNIZE_SPEECH,
+                ACTION_PICK_RAW,
+                ACTION_PICK_DATA,
+                OPEN_DOCUMENT,
+                GET_CONTENT,
+                USB_DEVICE_ATTACHED,
+                ACTION_SEND,
+                HOME,
+                MOBILE_NETWORK_SETTINGS);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index f5ec595..ecafdfd 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -249,24 +249,6 @@
     }
 
     /**
-     * @return the current startable state.
-     */
-    public boolean isStartable() {
-        synchronized (mLock) {
-            return mStartableState.isStartable();
-        }
-    }
-
-    /**
-     * @return Whether the package is still being loaded or has been fully loaded.
-     */
-    public boolean isLoading() {
-        synchronized (mLock) {
-            return mLoadingState.isLoading();
-        }
-    }
-
-    /**
      * @return all current states in a Parcelable.
      */
     public IncrementalStatesInfo getIncrementalStatesInfo() {
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 15e1d52..7bf7042 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -59,6 +59,7 @@
 import com.android.server.utils.Snappable;
 import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
 import com.android.server.utils.WatchedSparseArray;
 import com.android.server.utils.WatchedSparseBooleanArray;
 import com.android.server.utils.Watcher;
@@ -123,6 +124,7 @@
     private final CookiePersistence mCookiePersistence;
 
     /** State for uninstalled instant apps */
+    @Watched
     @GuardedBy("mService.mLock")
     private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
 
@@ -132,10 +134,12 @@
      * The value is a set of instant app UIDs.
      * UserID -> TargetAppId -> InstantAppId
      */
+    @Watched
     @GuardedBy("mService.mLock")
     private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
 
     /** The set of all installed instant apps. UserID -> AppID */
+    @Watched
     @GuardedBy("mService.mLock")
     private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
 
@@ -189,6 +193,7 @@
         mUninstalledInstantApps.registerObserver(mObserver);
         mInstantGrants.registerObserver(mObserver);
         mInstalledInstantAppUids.registerObserver(mObserver);
+        Watchable.verifyWatchedAttributes(this, mObserver);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c4a23f9..f240d85 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1316,10 +1316,6 @@
                 } finally {
                     mListeners.finishBroadcast();
                 }
-                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-                pmi.registerInstalledLoadingProgressCallback(packageName,
-                        new PackageLoadingProgressCallback(packageName, user),
-                        user.getIdentifier());
                 super.onPackageAdded(packageName, uid);
             }
 
@@ -1542,38 +1538,5 @@
                 checkCallbackCount();
             }
         }
-
-        class PackageLoadingProgressCallback extends
-                PackageManagerInternal.InstalledLoadingProgressCallback {
-            private String mPackageName;
-            private UserHandle mUser;
-
-            PackageLoadingProgressCallback(String packageName, UserHandle user) {
-                super(mCallbackHandler);
-                mPackageName = packageName;
-                mUser = user;
-            }
-
-            @Override
-            public void onLoadingProgressChanged(float progress) {
-                final int n = mListeners.beginBroadcast();
-                try {
-                    for (int i = 0; i < n; i++) {
-                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
-                            continue;
-                        }
-                        try {
-                            listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
-                        } catch (RemoteException re) {
-                            Slog.d(TAG, "Callback failed ", re);
-                        }
-                    }
-                } finally {
-                    mListeners.finishBroadcast();
-                }
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 6485c0c..d851e6c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -401,11 +401,16 @@
                 String[] abiList = (cpuAbiOverride != null)
                         ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
 
-                // Enable gross and lame hacks for apps that are built with old
-                // SDK tools. We must scan their APKs for renderscript bitcode and
-                // not launch them if it's present. Don't bother checking on devices
-                // that don't have 64 bit support.
+                // If an app that contains RenderScript has target API level < 21, it needs to run
+                // with 32-bit ABI, and its APK file will contain a ".bc" file.
+                // If an app that contains RenderScript has target API level >= 21, it can run with
+                // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
+                // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
+                // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
+                // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
+                // we will fail the installation for such an app because it won't be able to run.
                 boolean needsRenderScriptOverride = false;
+                // No need to check if the device only supports 32-bit
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
                         && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                     if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
@@ -414,7 +419,8 @@
                     } else {
                         throw new PackageManagerException(
                                 INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
-                                "Apks with renderscript are not supported on 64-bit only devices");
+                                "Apps that contain RenderScript with target API level < 21 are not "
+                                        + "supported on 64-bit only platforms");
                     }
                 }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f444ed6..0c143c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.Intent.ACTION_MAIN;
-import static android.content.Intent.CATEGORY_BROWSABLE;
 import static android.content.Intent.CATEGORY_DEFAULT;
 import static android.content.Intent.CATEGORY_HOME;
 import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
@@ -68,11 +67,7 @@
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_APEX;
@@ -177,6 +172,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -197,6 +193,7 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ModuleInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
@@ -238,6 +235,7 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.IArtManager;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
@@ -377,11 +375,6 @@
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.dex.ViewCompiler;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.PackageParser2;
@@ -395,6 +388,11 @@
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
 import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -404,7 +402,6 @@
 import com.android.server.utils.Watched;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedSparseBooleanArray;
-import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -466,7 +463,6 @@
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -1753,6 +1749,11 @@
         public AndroidPackage getPackage(@NonNull String packageName) {
             return getPackageLocked(packageName);
         }
+
+        @Override
+        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+            return mPmInternal.filterAppAccess(packageName, callingUid, userId);
+        }
     }
 
     /**
@@ -3622,8 +3623,6 @@
             final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
             final int userId = UserHandle.getUserId(uid);
             final int appId = UserHandle.getAppId(uid);
-            enforceCrossUserPermission(callingUid, userId,
-                    /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid");
             return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
         }
 
@@ -5523,19 +5522,19 @@
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional,
             @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId) {
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
         requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
-                statusReceiver, userId, mInjector.getBackgroundExecutor(),
+                onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
                 mInjector.getBackgroundHandler());
     }
 
     private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Type int optional,
-            @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
-            @NonNull Handler handler) {
+            @Checksum.Type int optional, @Checksum.Type int required,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+            @NonNull Executor executor, @NonNull Handler handler) {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(handler);
 
@@ -5571,7 +5570,7 @@
                     () -> mInjector.getIncrementalManager(),
                     () -> mPmInternal);
             ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
-                    trustedCerts, statusReceiver, injector);
+                    trustedCerts, onChecksumsReadyListener, injector);
         });
     }
 
@@ -5769,8 +5768,8 @@
                 (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
                 (i, pm) -> (IncrementalManager)
                         i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
-                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
-                        RoleManager.class)),
+                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
+                        () -> LocalServices.getService(UserManagerInternal.class)),
                 (i, pm) -> new DisplayMetrics(),
                 (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
                         i.getDisplayMetrics(), pm.mCacheDir,
@@ -5946,6 +5945,21 @@
         }
     }
 
+    // Link watchables to the class
+    private void registerObserver() {
+        mPackages.registerObserver(mWatcher);
+        mSharedLibraries.registerObserver(mWatcher);
+        mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
+        mInstrumentation.registerObserver(mWatcher);
+        mWebInstantAppsDisabled.registerObserver(mWatcher);
+        mAppsFilter.registerObserver(mWatcher);
+        mInstantAppRegistry.registerObserver(mWatcher);
+        mSettings.registerObserver(mWatcher);
+        // If neither "build" attribute is true then this may be a mockito test, and verification
+        // can fail as a false positive.
+        Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+    }
+
     /**
      * A extremely minimal constructor designed to start up a PackageManagerService instance for
      * testing.
@@ -6029,15 +6043,7 @@
         sSnapshotCorked = true;
         mLiveComputer = createLiveComputer();
         mSnapshotComputer = mLiveComputer;
-
-        // Link up the watchers
-        mPackages.registerObserver(mWatcher);
-        mSharedLibraries.registerObserver(mWatcher);
-        mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
-        mInstrumentation.registerObserver(mWatcher);
-        mWebInstantAppsDisabled.registerObserver(mWatcher);
-        mAppsFilter.registerObserver(mWatcher);
-        Watchable.verifyWatchedAttributes(this, mWatcher);
+        registerObserver();
 
         mPackages.putAll(testParams.packages);
         mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
@@ -6191,15 +6197,6 @@
         mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
         mDomainVerificationManager.setConnection(mDomainVerificationConnection);
 
-        // Link up the watchers
-        mPackages.registerObserver(mWatcher);
-        mSharedLibraries.registerObserver(mWatcher);
-        mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
-        mInstrumentation.registerObserver(mWatcher);
-        mWebInstantAppsDisabled.registerObserver(mWatcher);
-        mAppsFilter.registerObserver(mWatcher);
-        Watchable.verifyWatchedAttributes(this, mWatcher);
-
         // Create the computer as soon as the state objects have been installed.  The
         // cached computer is the same as the live computer until the end of the
         // constructor, at which time the invalidation method updates it.  The cache is
@@ -6208,6 +6205,7 @@
         sSnapshotCorked = true;
         mLiveComputer = createLiveComputer();
         mSnapshotComputer = mLiveComputer;
+        registerObserver();
 
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
@@ -16164,8 +16162,7 @@
     @Deprecated
     @Override
     public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
-        mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
-        return true;
+        return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
     }
 
     @Deprecated
@@ -18127,11 +18124,8 @@
                             if (libPs == null) {
                                 continue;
                             }
-                            final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
-                            if (overlayPaths != null) {
-                                ps.setOverlayPathsForLibrary(sharedLib.getName(),
-                                        Arrays.asList(overlayPaths), currentUserId);
-                            }
+                            ps.setOverlayPathsForLibrary(sharedLib.getName(),
+                                    libPs.getOverlayPaths(currentUserId), currentUserId);
                         }
                     }
                 }
@@ -19168,6 +19162,8 @@
                     extras, 0 /*flags*/,
                     null /*targetPackage*/, null /*finishedReceiver*/,
                     mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null);
+            // Unregister progress listener
+            mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
             // Unregister health listener as it will always be healthy from now
             mIncrementalManager.unregisterHealthListener(codePath);
         }
@@ -23939,7 +23935,8 @@
                 writer.println("Domain verification status:");
                 writer.increaseIndent();
                 try {
-                    mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+                    mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
+                            mSettings::getPackageLPr);
                 } catch (PackageManager.NameNotFoundException e) {
                     pw.println("Failure printing domain verification information");
                     Slog.e(TAG, "Failure printing domain verification information", e);
@@ -26619,34 +26616,19 @@
 
         @Override
         public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
-                @Nullable List<String> overlayPackageNames,
-                @NonNull Collection<String> outUpdatedPackageNames) {
+                @Nullable OverlayPaths overlayPaths,
+                @NonNull Set<String> outUpdatedPackageNames) {
+            boolean modified = false;
             synchronized (mLock) {
                 final AndroidPackage targetPkg = mPackages.get(targetPackageName);
                 if (targetPackageName == null || targetPkg == null) {
                     Slog.e(TAG, "failed to find package " + targetPackageName);
                     return false;
                 }
-                ArrayList<String> overlayPaths = null;
-                if (overlayPackageNames != null && overlayPackageNames.size() > 0) {
-                    final int N = overlayPackageNames.size();
-                    overlayPaths = new ArrayList<>(N);
-                    for (int i = 0; i < N; i++) {
-                        final String packageName = overlayPackageNames.get(i);
-                        final AndroidPackage pkg = mPackages.get(packageName);
-                        if (pkg == null) {
-                            Slog.e(TAG, "failed to find package " + packageName);
-                            return false;
-                        }
-                        overlayPaths.add(pkg.getBaseApkPath());
-                    }
-                }
 
-                ArraySet<String> updatedPackageNames = null;
                 if (targetPkg.getLibraryNames() != null) {
                     // Set the overlay paths for dependencies of the shared library.
-                    updatedPackageNames = new ArraySet<>();
-                    for (String libName : targetPkg.getLibraryNames()) {
+                    for (final String libName : targetPkg.getLibraryNames()) {
                         final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
                                 SharedLibraryInfo.VERSION_UNDEFINED);
                         if (info == null) {
@@ -26657,28 +26639,30 @@
                         if (dependents == null) {
                             continue;
                         }
-                        for (VersionedPackage dependent : dependents) {
+                        for (final VersionedPackage dependent : dependents) {
                             final PackageSetting ps = mSettings.getPackageLPr(
                                     dependent.getPackageName());
                             if (ps == null) {
                                 continue;
                             }
-                            ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
-                            updatedPackageNames.add(dependent.getPackageName());
+                            if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) {
+                                outUpdatedPackageNames.add(dependent.getPackageName());
+                                modified = true;
+                            }
                         }
                     }
                 }
 
                 final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
-                ps.setOverlayPaths(overlayPaths, userId);
-
-                outUpdatedPackageNames.add(targetPackageName);
-                if (updatedPackageNames != null) {
-                    outUpdatedPackageNames.addAll(updatedPackageNames);
+                if (ps.setOverlayPaths(overlayPaths, userId)) {
+                    outUpdatedPackageNames.add(targetPackageName);
+                    modified = true;
                 }
             }
 
-            invalidatePackageInfoCache();
+            if (modified) {
+                invalidatePackageInfoCache();
+            }
             return true;
         }
 
@@ -27045,47 +27029,6 @@
         }
 
         @Override
-        public boolean registerInstalledLoadingProgressCallback(String packageName,
-                PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
-            final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
-                    userId);
-            if (ps == null) {
-                return false;
-            }
-            if (!ps.isPackageLoading()) {
-                Slog.w(TAG,
-                        "Failed registering loading progress callback. Package is fully loaded.");
-                return false;
-            }
-            if (mIncrementalManager == null) {
-                Slog.w(TAG,
-                        "Failed registering loading progress callback. Incremental is not enabled");
-                return false;
-            }
-            return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
-                    (IPackageLoadingProgressCallback) callback.getBinder());
-        }
-
-        @Override
-        public boolean unregisterInstalledLoadingProgressCallback(String packageName,
-                PackageManagerInternal.InstalledLoadingProgressCallback callback) {
-            final PackageSetting ps;
-            synchronized (mLock) {
-                ps = mSettings.getPackageLPr(packageName);
-                if (ps == null) {
-                    Slog.w(TAG, "Failed unregistering loading progress callback. Package "
-                            + packageName + " is not installed");
-                    return false;
-                }
-            }
-            if (mIncrementalManager == null) {
-                return false;
-            }
-            return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
-                    (IPackageLoadingProgressCallback) callback.getBinder());
-        }
-
-        @Override
         public IncrementalStatesInfo getIncrementalStatesInfo(
                 @NonNull String packageName, int filterCallingUid, int userId) {
             final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
@@ -27110,12 +27053,14 @@
             ps.setStatesOnCrashOrAnr();
         }
 
+        @Override
         public void requestChecksums(@NonNull String packageName, boolean includeSplits,
                 @Checksum.Type int optional, @Checksum.Type int required,
-                @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+                @Nullable List trustedInstallers,
+                @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
                 @NonNull Executor executor, @NonNull Handler handler) {
             requestChecksumsInternal(packageName, includeSplits, optional, required,
-                    trustedInstallers, statusReceiver, userId, executor, handler);
+                    trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 69e84b5..ca5d2b4 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -344,8 +344,8 @@
                     installSource.originatingPackageName);
             proto.end(sourceToken);
         }
-        proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
-        proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
+        proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable());
+        proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading());
         writeUsersInfoToProto(proto, PackageProto.USERS);
         writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
         proto.end(packageToken);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 8aa553d..5364cbf 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,13 +26,12 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.UninstallReason;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.os.PersistableBundle;
 import android.os.incremental.IncrementalManager;
 import android.service.pm.PackageProto;
@@ -42,13 +41,10 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.File;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -331,21 +327,20 @@
         modifyUserState(userId).uninstallReason = uninstallReason;
     }
 
-    void setOverlayPaths(List<String> overlayPaths, int userId) {
-        modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
-            overlayPaths.toArray(new String[overlayPaths.size()]));
+    boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
+        return modifyUserState(userId).setOverlayPaths(overlayPaths);
     }
 
-    String[] getOverlayPaths(int userId) {
+    OverlayPaths getOverlayPaths(int userId) {
         return readUserState(userId).getOverlayPaths();
     }
 
-    void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
-        modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
-                overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
+    boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
+            int userId) {
+        return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
     }
 
-    Map<String, String[]> getOverlayPathsForLibrary(int userId) {
+    Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
         return readUserState(userId).getSharedLibraryOverlayPaths();
     }
 
@@ -731,14 +726,14 @@
      * @return True if package is startable, false otherwise.
      */
     public boolean isPackageStartable() {
-        return incrementalStates.isStartable();
+        return getIncrementalStates().isStartable();
     }
 
     /**
      * @return True if package is still being loaded, false if the package is fully loaded.
      */
     public boolean isPackageLoading() {
-        return incrementalStates.isLoading();
+        return getIncrementalStates().isLoading();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
 import android.app.admin.SecurityLog;
 import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -39,7 +33,6 @@
 import com.android.internal.os.BackgroundThread;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -109,26 +102,23 @@
         // Capturing local loggingInfo to still log even if hash was invalidated.
         try {
             pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
-                    new IntentSender((IIntentSender) new IIntentSender.Stub() {
+                    new IOnChecksumsReadyListener.Stub() {
                         @Override
-                        public void send(int code, Intent intent, String resolvedType,
-                                IBinder allowlistToken, IIntentReceiver finishedReceiver,
-                                String requiredPermission, Bundle options) {
-                            processChecksums(loggingInfo, intent);
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            processChecksums(loggingInfo, checksums);
                         }
-                    }), context.getUserId(), mExecutor, this);
+                    }, context.getUserId(),
+                    mExecutor, this);
         } catch (Throwable t) {
             Slog.e(TAG, "requestChecksums() failed", t);
             enqueueProcessChecksum(loggingInfo, null);
         }
     }
 
-    void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
-        Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
-        ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
-                ApkChecksum[].class);
-
-        for (ApkChecksum checksum : checksums) {
+    void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+        for (int i = 0, size = checksums.size(); i < size; ++i) {
+            ApkChecksum checksum = checksums.get(i);
             if (checksum.getType() == CHECKSUM_TYPE) {
                 processChecksum(loggingInfo, checksum.getValue());
                 return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
         if (!changed) {
             return false;
         }
-        if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+        if (!BundleUtils.isEmpty(restrictions)) {
             mUserRestrictions.put(userId, restrictions);
         } else {
             mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fb033e6..a8a6bce 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -40,6 +40,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
 import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageUserState;
@@ -49,6 +50,7 @@
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -105,9 +107,6 @@
 import com.android.server.LocalServices;
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationPersistence;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -115,6 +114,9 @@
 import com.android.server.pm.permission.LegacyPermissionSettings;
 import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationPersistence;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.utils.Watchable;
@@ -487,7 +489,7 @@
     // App-link priority tracking, per-user
     @NonNull
     @Watched
-    final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
+    private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
 
     final StringBuilder mReadMessages = new StringBuilder();
 
@@ -552,6 +554,7 @@
         mAppIds.registerObserver(mObserver);
         mOtherAppIds.registerObserver(mObserver);
         mRenamedPackages.registerObserver(mObserver);
+        mNextAppLinkGeneration.registerObserver(mObserver);
         mDefaultBrowserApp.registerObserver(mObserver);
 
         Watchable.verifyWatchedAttributes(this, mObserver);
@@ -602,6 +605,7 @@
         mAppIds.registerObserver(mObserver);
         mOtherAppIds.registerObserver(mObserver);
         mRenamedPackages.registerObserver(mObserver);
+        mNextAppLinkGeneration.registerObserver(mObserver);
         mDefaultBrowserApp.registerObserver(mObserver);
 
         Watchable.verifyWatchedAttributes(this, mObserver);
@@ -649,6 +653,7 @@
         mPastSignatures.addAll(r.mPastSignatures);
         mKeySetRefs.putAll(r.mKeySetRefs);
         mRenamedPackages.snapshot(r.mRenamedPackages);
+        mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
         mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
         // mReadMessages
         mPendingPackages.addAll(r.mPendingPackages);
@@ -2707,7 +2712,6 @@
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
-        mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
         writeMimeGroupLPr(serializer, pkg.mimeGroups);
 
         serializer.endTag(null, "package");
@@ -4686,26 +4690,58 @@
                 }
             }
 
-            String[] overlayPaths = ps.getOverlayPaths(user.id);
-            if (overlayPaths != null && overlayPaths.length > 0) {
-                pw.print(prefix); pw.println("    overlay paths:");
-                for (String path : overlayPaths) {
-                    pw.print(prefix); pw.print("      "); pw.println(path);
+            final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
+            if (overlayPaths != null) {
+                if (!overlayPaths.getOverlayPaths().isEmpty()) {
+                    pw.print(prefix);
+                    pw.println("    overlay paths:");
+                    for (String path : overlayPaths.getOverlayPaths()) {
+                        pw.print(prefix);
+                        pw.print("      ");
+                        pw.println(path);
+                    }
+                }
+                if (!overlayPaths.getResourceDirs().isEmpty()) {
+                    pw.print(prefix);
+                    pw.println("    legacy overlay paths:");
+                    for (String path : overlayPaths.getResourceDirs()) {
+                        pw.print(prefix);
+                        pw.print("      ");
+                        pw.println(path);
+                    }
                 }
             }
 
-            Map<String, String[]> sharedLibraryOverlayPaths =
+            final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
                     ps.getOverlayPathsForLibrary(user.id);
             if (sharedLibraryOverlayPaths != null) {
-                for (Map.Entry<String, String[]> libOverlayPaths :
+                for (Map.Entry<String, OverlayPaths> libOverlayPaths :
                         sharedLibraryOverlayPaths.entrySet()) {
-                    if (libOverlayPaths.getValue() == null) {
+                    final OverlayPaths paths = libOverlayPaths.getValue();
+                    if (paths == null) {
                         continue;
                     }
-                    pw.print(prefix); pw.print("    ");
-                    pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
-                    for (String path : libOverlayPaths.getValue()) {
-                        pw.print(prefix); pw.print("      "); pw.println(path);
+                    if (!paths.getOverlayPaths().isEmpty()) {
+                        pw.print(prefix);
+                        pw.println("    ");
+                        pw.print(libOverlayPaths.getKey());
+                        pw.println(" overlay paths:");
+                        for (String path : paths.getOverlayPaths()) {
+                            pw.print(prefix);
+                            pw.print("        ");
+                            pw.println(path);
+                        }
+                    }
+                    if (!paths.getResourceDirs().isEmpty()) {
+                        pw.print(prefix);
+                        pw.println("      ");
+                        pw.print(libOverlayPaths.getKey());
+                        pw.println(" legacy overlay paths:");
+                        for (String path : paths.getResourceDirs()) {
+                            pw.print(prefix);
+                            pw.print("      ");
+                            pw.println(path);
+                        }
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 89729b5..9b092c0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1535,6 +1535,27 @@
         pw.println(")");
     }
 
+    public void dumpShortcuts(@NonNull PrintWriter pw, int matchFlags) {
+        final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
+        final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
+        final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
+        final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+
+        final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+                | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+                | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+                | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+
+        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
+        final int size = shortcuts.size();
+        for (int i = 0; i < size; i++) {
+            final ShortcutInfo si = shortcuts.valueAt(i);
+            if ((si.getFlags() & shortcutFlags) != 0) {
+                pw.println(si.toDumpString(""));
+            }
+        }
+    }
+
     @Override
     public JSONObject dumpCheckin(boolean clear) throws JSONException {
         final JSONObject result = super.dumpCheckin(clear);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3c4457d..863e3fe5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,6 +40,7 @@
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.IShortcutService;
 import android.content.pm.LauncherApps;
@@ -4556,6 +4557,10 @@
 
         private int mUserId = UserHandle.USER_SYSTEM;
 
+        private int mShortcutMatchFlags = ShortcutManager.FLAG_MATCH_CACHED
+                | ShortcutManager.FLAG_MATCH_DYNAMIC | ShortcutManager.FLAG_MATCH_MANIFEST
+                | ShortcutManager.FLAG_MATCH_PINNED;
+
         private void parseOptionsLocked(boolean takeUser)
                 throws CommandException {
             String opt;
@@ -4571,6 +4576,9 @@
                             break;
                         }
                         // fallthrough
+                    case "--flags":
+                        mShortcutMatchFlags = Integer.parseInt(getNextArgRequired());
+                        break;
                     default:
                         throw new CommandException("Unknown option: " + opt);
                 }
@@ -4606,9 +4614,15 @@
                     case "clear-shortcuts":
                         handleClearShortcuts();
                         break;
+                    case "get-shortcuts":
+                        handleGetShortcuts();
+                        break;
                     case "verify-states": // hidden command to verify various internal states.
                         handleVerifyStates();
                         break;
+                    case "has-shortcut-access":
+                        handleHasShortcutAccess();
+                        break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -4640,7 +4654,7 @@
             pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]");
             pw.println("    Show the default launcher");
             pw.println("    Note: This command is deprecated. Callers should query the default"
-                    + " launcher directly from RoleManager instead.");
+                    + " launcher from RoleManager instead.");
             pw.println();
             pw.println("cmd shortcut unload-user [--user USER_ID]");
             pw.println("    Unload a user from the memory");
@@ -4649,6 +4663,13 @@
             pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
             pw.println("    Remove all shortcuts from a package, including pinned shortcuts");
             pw.println();
+            pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE");
+            pw.println("    Show the shortcuts for a package that match the given flags");
+            pw.println();
+            pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE");
+            pw.println("    Prints \"true\" if the package can access shortcuts,"
+                    + " \"false\" otherwise");
+            pw.println();
         }
 
         private void handleResetThrottling() throws CommandException {
@@ -4693,11 +4714,24 @@
         private void handleGetDefaultLauncher() throws CommandException {
             synchronized (mLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
+
+                final String defaultLauncher = getDefaultLauncher(mUserId);
+                if (defaultLauncher == null) {
+                    throw new CommandException(
+                            "Failed to get the default launcher for user " + mUserId);
+                }
+
+                // Get the class name of the component from PM to keep the old behaviour.
                 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
-                // Default launcher from package manager.
-                final ComponentName defaultLauncher = mPackageManagerInternal
-                        .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId));
-                getOutPrintWriter().println("Launcher: " + defaultLauncher);
+                mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates,
+                        getParentOrSelfUserId(mUserId));
+                for (ResolveInfo ri : allHomeCandidates) {
+                    final ComponentInfo ci = ri.getComponentInfo();
+                    if (ci.packageName.equals(defaultLauncher)) {
+                        getOutPrintWriter().println("Launcher: " + ci.getComponentName());
+                        break;
+                    }
+                }
             }
         }
 
@@ -4723,6 +4757,24 @@
             }
         }
 
+        private void handleGetShortcuts() throws CommandException {
+            synchronized (mLock) {
+                parseOptionsLocked(/* takeUser =*/ true);
+                final String packageName = getNextArgRequired();
+
+                Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+                        + mShortcutMatchFlags + ", package=" + packageName);
+
+                final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
+                final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName);
+                if (p == null) {
+                    return;
+                }
+
+                p.dumpShortcuts(getOutPrintWriter(), mShortcutMatchFlags);
+            }
+        }
+
         private void handleVerifyStates() throws CommandException {
             try {
                 verifyStatesForce(); // This will throw when there's an issue.
@@ -4730,6 +4782,16 @@
                 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
             }
         }
+
+        private void handleHasShortcutAccess() throws CommandException {
+            synchronized (mLock) {
+                parseOptionsLocked(/* takeUser =*/ true);
+                final String packageName = getNextArgRequired();
+
+                boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId);
+                getOutPrintWriter().println(Boolean.toString(shortcutAccess));
+            }
+        }
     }
 
     // === Unit test support ===
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
      * Gets all {@link UserInfo UserInfos}.
      */
     public abstract @NonNull UserInfo[] getUserInfos();
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}.
+     */
+    public abstract void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int parentUserId, @UserIdInt int profileUserId);
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a807777..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
 import android.os.UserManager.EnforcingUser;
 import android.os.UserManager.QuietModeFlag;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
         final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
         final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
 
-        if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+        if (BundleUtils.isEmpty(global) && local.isEmpty()) {
             // Common case first.
             return baseRestrictions;
         }
-        final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+        final Bundle effective = BundleUtils.clone(baseRestrictions);
         UserRestrictionsUtils.merge(effective, global);
         UserRestrictionsUtils.merge(effective, local.mergeAll());
 
@@ -2105,7 +2107,7 @@
     @Override
     public Bundle getUserRestrictions(@UserIdInt int userId) {
         checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
-        return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+        return BundleUtils.clone(getEffectiveUserRestrictions(userId));
     }
 
     @Override
@@ -2129,7 +2131,7 @@
         synchronized (mRestrictionsLock) {
             // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
             // a copy.
-            final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            final Bundle newRestrictions = BundleUtils.clone(
                     mBaseUserRestrictions.getRestrictions(userId));
             newRestrictions.putBoolean(key, value);
 
@@ -2727,7 +2729,7 @@
         if (userVersion < 7) {
             // Previously only one user could enforce global restrictions, now it is per-user.
             synchronized (mRestrictionsLock) {
-                if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+                if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
                         && mDeviceOwnerUserId != UserHandle.USER_NULL) {
                     mDevicePolicyGlobalUserRestrictions.updateRestrictions(
                             mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
             t.traceBegin("PM.onNewUserCreated-" + userId);
             mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
+            applyDefaultUserSettings(userTypeDetails, userId);
+            setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
                 // UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
         return userInfo;
     }
 
+    private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+        final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+        final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+        if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+            return;
+        }
+
+        final int systemSettingsSize = systemSettings.size();
+        final String[] systemSettingsArray = systemSettings.keySet().toArray(
+                new String[systemSettingsSize]);
+        for (int i = 0; i < systemSettingsSize; i++) {
+            final String setting = systemSettingsArray[i];
+            if (!Settings.System.putStringForUser(
+                    mContext.getContentResolver(), setting, systemSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+            }
+        }
+
+        final int secureSettingsSize = secureSettings.size();
+        final String[] secureSettingsArray = secureSettings.keySet().toArray(
+                new String[secureSettingsSize]);
+        for (int i = 0; i < secureSettingsSize; i++) {
+            final String setting = secureSettingsArray[i];
+            if (!Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(), setting, secureSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+            }
+        }
+    }
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+     */
+    private void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+            Bundle profileRestrictions, @UserIdInt int parentUserId) {
+        if (profileDetails == null || !profileDetails.isProfile()) {
+            return;
+        }
+        final List<DefaultCrossProfileIntentFilter> filters =
+                profileDetails.getDefaultCrossProfileIntentFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+
+        // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+        final boolean disallowSharingIntoProfile =
+                profileRestrictions.getBoolean(
+                        UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+                        /* defaultValue = */ false);
+        final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+        for (int i = 0; i < size; i++) {
+            final DefaultCrossProfileIntentFilter filter =
+                    profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+            if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+                continue;
+            }
+            if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+                        filter.flags);
+            } else {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+                        filter.flags);
+            }
+        }
+    }
+
     /**
      * Finds and converts a previously pre-created user into a regular user, if possible.
      *
@@ -5462,6 +5539,15 @@
                 return allInfos;
             }
         }
+
+        @Override
+        public void setDefaultCrossProfileIntentFilters(
+                @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+            final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+            final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+            UserManagerService.this.setDefaultCrossProfileIntentFilters(
+                    profileUserId, userTypeDetails, restrictions, parentUserId);
+        }
     }
 
     /**
@@ -5726,8 +5812,8 @@
 
         // merge existing base restrictions with the new type's default restrictions
         synchronized (mRestrictionsLock) {
-            if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
-                final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+                final Bundle newRestrictions = BundleUtils.clone(
                         mBaseUserRestrictions.getRestrictions(userInfo.id));
                 UserRestrictionsUtils.merge(newRestrictions,
                         newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 
 import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
         return in != null ? in : new Bundle();
     }
 
-    public static boolean isEmpty(@Nullable Bundle in) {
-        return (in == null) || (in.size() == 0);
-    }
-
     /**
      * Returns {@code true} if given bundle is not null and contains {@code true} for a given
      * restriction.
@@ -396,17 +393,6 @@
         return in != null && in.getBoolean(restriction);
     }
 
-    /**
-     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
-     * bundle.
-     *
-     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
-     * {@link Bundle#EMPTY})
-     */
-    public static @NonNull Bundle clone(@Nullable Bundle in) {
-        return (in != null) ? new Bundle(in) : new Bundle();
-    }
-
     public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
         Objects.requireNonNull(dest);
         Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
         if (a == b) {
             return true;
         }
-        if (isEmpty(a)) {
-            return isEmpty(b);
+        if (BundleUtils.isEmpty(a)) {
+            return BundleUtils.isEmpty(b);
         }
-        if (isEmpty(b)) {
+        if (BundleUtils.isEmpty(b)) {
             return false;
         }
         for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
 import android.os.UserManager;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
      */
     private final @Nullable Bundle mDefaultRestrictions;
 
+    /**
+     * List of {@link android.provider.Settings.System} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSystemSettings;
+
+    /**
+     * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSecureSettings;
+
+    /**
+     * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+     * profiles.
+     */
+    private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
 
     // Fields for profiles only, controlling the nature of their badges.
     // All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
             int iconBadge, int badgePlain, int badgeNoBackground,
             @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
             @Nullable int[] darkThemeBadgeColors,
-            @Nullable Bundle defaultRestrictions) {
+            @Nullable Bundle defaultRestrictions,
+            @Nullable Bundle defaultSystemSettings,
+            @Nullable Bundle defaultSecureSettings,
+            @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
         this.mName = name;
         this.mEnabled = enabled;
         this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
         this.mBaseType = baseType;
         this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
         this.mDefaultRestrictions = defaultRestrictions;
+        this.mDefaultSystemSettings = defaultSystemSettings;
+        this.mDefaultSecureSettings = defaultSecureSettings;
+        this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
 
         this.mIconBadge = iconBadge;
         this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
         return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
     }
 
-    /** Returns a Bundle representing the default user restrictions. */
+    /** Returns a {@link Bundle} representing the default user restrictions. */
     @NonNull Bundle getDefaultRestrictions() {
-        return UserRestrictionsUtils.clone(mDefaultRestrictions);
+        return BundleUtils.clone(mDefaultRestrictions);
     }
 
     /** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
         UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
     }
 
+    /** Returns a {@link Bundle} representing the default system settings. */
+    @NonNull Bundle getDefaultSystemSettings() {
+        return BundleUtils.clone(mDefaultSystemSettings);
+    }
+
+    /** Returns a {@link Bundle} representing the default secure settings. */
+    @NonNull Bundle getDefaultSecureSettings() {
+        return BundleUtils.clone(mDefaultSecureSettings);
+    }
+
+    /** Returns a list of default cross profile intent filters. */
+    @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+        return mDefaultCrossProfileIntentFilters != null
+                ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+                : Collections.emptyList();
+    }
+
+
     /** Dumps details of the UserTypeDetails. Do not parse this. */
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
         private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
         private int mDefaultUserInfoPropertyFlags = 0;
         private @Nullable Bundle mDefaultRestrictions = null;
+        private @Nullable Bundle mDefaultSystemSettings = null;
+        private @Nullable Bundle mDefaultSecureSettings = null;
+        private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+                null;
         private boolean mEnabled = true;
         private int mLabel = Resources.ID_NULL;
         private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
             return this;
         }
 
+        public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+            mDefaultSystemSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+            mDefaultSecureSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultCrossProfileIntentFilters(
+                @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+            mDefaultCrossProfileIntentFilters = intentFilters;
+            return this;
+        }
+
         @UserInfoFlag int getBaseType() {
             return mBaseType;
         }
@@ -425,17 +491,28 @@
                 Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
                         "UserTypeDetails " + mName + " has badge but no badgeColors.");
             }
+            if (!isProfile()) {
+                Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+                                || mDefaultCrossProfileIntentFilters.isEmpty(),
+                        "UserTypeDetails %s has a non empty "
+                                + "defaultCrossProfileIntentFilters", mName);
+            }
             return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
                     mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
                     mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
                     mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
-                    mDefaultRestrictions);
+                    mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+                    mDefaultCrossProfileIntentFilters);
         }
 
         private boolean hasBadge() {
             return mIconBadge != Resources.ID_NULL;
         }
 
+        private boolean isProfile() {
+            return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+        }
+
         // TODO(b/143784345): Refactor this when we clean up UserInfo.
         private boolean hasValidBaseType() {
             return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
                         com.android.internal.R.color.profile_badge_1_dark,
                         com.android.internal.R.color.profile_badge_2_dark,
                         com.android.internal.R.color.profile_badge_3_dark)
-                .setDefaultRestrictions(null);
+                .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+                .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+                .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
     }
 
     /**
@@ -256,12 +258,35 @@
         return restrictions;
     }
 
+    private static Bundle getDefaultManagedProfileRestrictions() {
+        final Bundle restrictions = new Bundle();
+        restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+        return restrictions;
+    }
+
+    private static Bundle getDefaultManagedProfileSecureSettings() {
+        // Only add String values to the bundle, settings are written as Strings eventually
+        final Bundle settings = new Bundle();
+        settings.putString(
+                android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+        settings.putString(
+                android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+        return settings;
+    }
+
+    private static List<DefaultCrossProfileIntentFilter>
+            getDefaultManagedCrossProfileIntentFilter() {
+        return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+    }
+
     /**
      * Reads the given xml parser to obtain device user-type customization, and updates the given
      * map of {@link UserTypeDetails.Builder}s accordingly.
      * <p>
      * The xml file can specify the attributes according to the set... methods below.
      */
+    // TODO(b/176973369): Add parsing logic to support custom settings/filters
+    //  in config_user_types.xml
     @VisibleForTesting
     static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
             XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
new file mode 100644
index 0000000..6cdd4df
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
@@ -0,0 +1,35 @@
+/*
+ * 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.pm.parsing.library;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/**
+ * Updates a package to remove dependency on android.net.ipsec.ike library.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidNetIpSecIkeUpdater extends PackageSharedLibraryUpdater {
+
+    private static final String LIBRARY_NAME = "android.net.ipsec.ike";
+
+    @Override
+    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+        removeLibrary(parsedPackage, LIBRARY_NAME);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 1405a7d..8a8a302 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -45,6 +45,9 @@
     static {
         final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
 
+        // Remove android.net.ipsec.ike library, it is added to boot classpath since Android S.
+        packageUpdaters.add(new AndroidNetIpSecIkeUpdater());
+
         // Remove com.google.android.maps library.
         packageUpdaters.add(new ComGoogleAndroidMapsUpdater());
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index af9978b..1925590 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -32,15 +32,30 @@
 
 import com.android.internal.util.CollectionUtils;
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
 import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
 
+@SuppressWarnings("PointlessBooleanExpression")
 public class DomainVerificationDebug {
 
+    // Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be
+    // enabled and checked in, without having everything be on or off.
+    public static final boolean DEBUG_ANY = false;
+
+    // Enable to turn on all logging. Requires enabling DEBUG_ANY.
+    public static final boolean DEBUG_ALL = false;
+
+    public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true);
+    public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false);
+    public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false);
+
     @NonNull
     private final DomainVerificationCollector mCollector;
 
@@ -50,7 +65,7 @@
 
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
             @Nullable @UserIdInt Integer userId,
-            @NonNull DomainVerificationService.Connection connection,
+            @NonNull Function<String, PackageSetting> pkgSettingFunction,
             @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
             throws NameNotFoundException {
         ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -61,7 +76,7 @@
             for (int index = 0; index < size; index++) {
                 DomainVerificationPkgState pkgState = stateMap.valueAt(index);
                 String pkgName = pkgState.getPackageName();
-                PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+                PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
                 if (pkgSetting == null || pkgSetting.getPkg() == null) {
                     continue;
                 }
@@ -77,7 +92,7 @@
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+            PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
             if (pkgSetting == null || pkgSetting.getPkg() == null) {
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
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 c521f82..275dd053 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
@@ -18,8 +18,10 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Process;
 
@@ -30,10 +32,17 @@
     @NonNull
     private final Context mContext;
 
+    @NonNull
+    private Callback mCallback;
+
     public DomainVerificationEnforcer(@NonNull Context context) {
         mContext = context;
     }
 
+    public void setCallback(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
     /**
      * Enforced when mutating any state from shell or internally in the system process.
      */
@@ -67,6 +76,11 @@
                             "Caller " + callingUid
                                     + " is not allowed to query domain verification state");
                 }
+
+                mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+                        Binder.getCallingPid(), callingUid,
+                        "Caller " + callingUid + " does not hold "
+                                + android.Manifest.permission.QUERY_ALL_PACKAGES);
                 break;
         }
     }
@@ -84,28 +98,42 @@
                 isAllowed = true;
                 break;
             default:
-                // TODO(b/159952358): Remove permission check? The component package should
-                //  have been checked when the verifier component was first scanned in PMS.
-                mContext.enforcePermission(
-                        android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
-                        Binder.getCallingPid(), callingUid,
-                        "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+                final int callingPid = Binder.getCallingPid();
+                boolean isLegacyVerificationAgent = false;
+                if (mContext.checkPermission(
+                        android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid,
+                        callingUid) != PackageManager.PERMISSION_GRANTED) {
+                    isLegacyVerificationAgent = mContext.checkPermission(
+                            android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                            callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+                    if (!isLegacyVerificationAgent) {
+                        throw new SecurityException("Caller " + callingUid + " does not hold "
+                                + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT);
+                    }
+                }
+
+                // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission
+                if (!isLegacyVerificationAgent) {
+                    mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+                            callingPid, callingUid, "Caller " + callingUid + " does not hold "
+                                    + android.Manifest.permission.QUERY_ALL_PACKAGES);
+                }
+
                 isAllowed = proxy.isCallerVerifier(callingUid);
                 break;
         }
 
         if (!isAllowed) {
             throw new SecurityException("Caller " + callingUid
-                    + " is not the approved domain verification agent, isVerifier = "
-                    + proxy.isCallerVerifier(callingUid));
+                    + " is not the approved domain verification agent");
         }
     }
 
     /**
      * Enforced when mutating user selection state inside an exposed API method.
      */
-    public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
-            @UserIdInt int targetUserId) throws SecurityException {
+    public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+            @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
         if (callingUserId != targetUserId) {
             mContext.enforcePermission(
                     Manifest.permission.INTERACT_ACROSS_USERS,
@@ -117,12 +145,51 @@
                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
                 Binder.getCallingPid(), callingUid,
                 "Caller is not allowed to edit user selections");
+
+        if (packageName == null) {
+            return true;
+        }
+
+        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
     }
 
-    public void callerIsLegacyUserSelector(int callingUid) {
+    public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId,
+            @NonNull String packageName, @UserIdInt int targetUserId) {
         mContext.enforcePermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
                 Binder.getCallingPid(), callingUid,
                 "Caller is not allowed to edit user state");
+
+        if (callingUserId != targetUserId) {
+            if (mContext.checkPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) {
+                // Legacy API did not enforce this, so for backwards compatibility, fail silently
+                return false;
+            }
+        }
+
+        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+    }
+
+    public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId,
+            @NonNull String packageName, @UserIdInt int targetUserId) {
+        if (callingUserId != targetUserId) {
+            // The legacy API enforces the _FULL variant, so maintain that here
+            mContext.enforcePermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    Binder.getCallingPid(), callingUid,
+                    "Caller is not allowed to edit other users");
+        }
+
+        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+    }
+
+    public interface Callback {
+        /**
+         * @return true if access to the given package should be filtered and the method failed as
+         * if the package was not installed
+         */
+        boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 7ad275a..50fd6e3 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -32,15 +32,16 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 
 public interface DomainVerificationManagerInternal extends DomainVerificationManager {
 
@@ -173,8 +174,10 @@
      * Set aside a legacy user selection that will be restored to a pending
      * {@link DomainVerificationPkgState} once it's added through
      * {@link #addPackage(PackageSetting)}.
+     *
+     * @return true if state changed successfully
      */
-    void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+    boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
 
     /**
      * Until the legacy APIs are entirely removed, returns the legacy state from the previously
@@ -183,20 +186,23 @@
     int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
 
     /**
-     * Serialize a legacy setting that wasn't attached yet.
-     * TODO: Does this even matter? Should consider for removal.
-     */
-    void writeLegacySettings(TypedXmlSerializer serializer, String name);
-
-    /**
      * Print the verification state and user selection state of a package.
      *
-     * @param packageName the package whose state to change, or all packages if none is specified
-     * @param userId      the specific user to print, or null to skip printing user selection
-     *                    states, supports {@link android.os.UserHandle#USER_ALL}
+     * @param packageName        the package whose state to change, or all packages if none is
+     *                           specified
+     * @param userId             the specific user to print, or null to skip printing user selection
+     *                           states, supports {@link android.os.UserHandle#USER_ALL}
+     * @param pkgSettingFunction the method by which to retrieve package data; if this is called
+     *                           from {@link com.android.server.pm.PackageManagerService}, it is
+     *                           expected to pass in the snapshot of {@link PackageSetting} objects,
+     *                           or if null is passed, the manager may decide to lock {@link
+     *                           com.android.server.pm.PackageManagerService} through {@link
+     *                           Connection#getPackageSettingLocked(String)}
      */
     void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+            @Nullable @UserIdInt Integer userId,
+            @Nullable Function<String, PackageSetting> pkgSettingFunction)
+            throws NameNotFoundException;
 
     @NonNull
     DomainVerificationShell getShell();
@@ -225,7 +231,8 @@
             throws IllegalArgumentException, NameNotFoundException;
 
 
-    interface Connection {
+    interface Connection extends DomainVerificationEnforcer.Callback,
+            Function<String, PackageSetting> {
 
         /**
          * Notify that a settings change has been made and that eventually
@@ -249,10 +256,19 @@
          */
         void schedule(int code, @Nullable Object object);
 
+        // TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid
+        //  locking package state at all. This can be as simple as removing this method in favor of
+        //  accepting a PackageSetting function in at every method call, although should probably
+        //  be abstracted to a wrapper class.
         @Nullable
         PackageSetting getPackageSettingLocked(@NonNull String pkgName);
 
         @Nullable
         AndroidPackage getPackageLocked(@NonNull String pkgName);
+
+        @Override
+        default PackageSetting apply(@NonNull String pkgName) {
+            return getPackageSettingLocked(pkgName);
+        }
     }
 }
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 53540c8..fa03274 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
@@ -47,12 +47,12 @@
 import com.android.server.SystemService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
 import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -64,13 +64,14 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 
 public class DomainVerificationService extends SystemService
         implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
 
     private static final String TAG = "DomainVerificationService";
 
-    public static final boolean DEBUG_APPROVAL = true;
+    public static final boolean DEBUG_APPROVAL = DomainVerificationDebug.DEBUG_APPROVAL;
 
     /**
      * The new user preference API for verifying domains marked autoVerify=true in
@@ -91,9 +92,9 @@
      * immediately attached once its available.
      * <p>
      * Generally this should be not accessed directly. Prefer calling {@link
-     * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+     * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}.
      *
-     * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+     * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)
      **/
     @GuardedBy("mLock")
     @NonNull
@@ -159,6 +160,7 @@
     @Override
     public void setConnection(@NonNull Connection connection) {
         mConnection = connection;
+        mEnforcer.setCallback(mConnection);
     }
 
     @NonNull
@@ -284,7 +286,7 @@
         mEnforcer.assertApprovedVerifier(callingUid, mProxy);
         synchronized (mLock) {
             DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
-                    true /* forAutoVerify */);
+                    true /* forAutoVerify */, callingUid, null /* userId */);
             ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
             for (String domain : domains) {
                 Integer previousState = stateMap.get(domain);
@@ -388,8 +390,10 @@
 
     public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
             boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
-        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
-                mConnection.getCallingUserId(), userId);
+        if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), packageName, userId)) {
+            throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+        }
         synchronized (mLock) {
             DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
             if (pkgState == null) {
@@ -454,11 +458,18 @@
     public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
             throws InvalidDomainSetException, NameNotFoundException {
-        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
-                mConnection.getCallingUserId(), userId);
         synchronized (mLock) {
+            final int callingUid = mConnection.getCallingUid();
+            // Pass null for package name here and do the app visibility enforcement inside
+            // getAndValidateAttachedLocked instead, since this has to fail with the same invalid
+            // ID reason if the target app is invisible
+            if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
+                    null /* packageName */, userId)) {
+                throw new InvalidDomainSetException(domainSetId, null,
+                        InvalidDomainSetException.REASON_ID_INVALID);
+            }
             DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
-                    false /* forAutoVerify */);
+                    false /* forAutoVerify */, callingUid, userId);
             DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
             if (enabled) {
                 userState.addHosts(domains);
@@ -541,8 +552,6 @@
                 userState.removeHosts(domains);
             }
         }
-
-        mConnection.scheduleWriteSettings();
     }
 
     @Nullable
@@ -557,8 +566,10 @@
     @Override
     public DomainVerificationUserSelection getDomainVerificationUserSelection(
             @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
-        mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
-                mConnection.getCallingUserId(), userId);
+        if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), packageName, userId)) {
+            throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+        }
         synchronized (mLock) {
             DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
             if (pkgState == null) {
@@ -845,22 +856,27 @@
     }
 
     @Override
-    public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
-        mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+    public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId,
+            int state) {
+        if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), packageName, userId)) {
+            return false;
+        }
         mLegacySettings.add(packageName, userId, state);
+        mConnection.scheduleWriteSettings();
+        return true;
     }
 
     @Override
     public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+        if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(),
+                mConnection.getCallingUserId(), packageName, userId)) {
+            return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        }
         return mLegacySettings.getUserState(packageName, userId);
     }
 
     @Override
-    public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
-
-    }
-
-    @Override
     public void clearPackage(@NonNull String packageName) {
         synchronized (mLock) {
             mAttachedPkgStates.remove(packageName);
@@ -890,9 +906,24 @@
 
     @Override
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+            @Nullable Integer userId) throws NameNotFoundException {
+        // This method is only used by DomainVerificationShell, which doesn't lock PMS, so it's
+        // safe to pass mConnection directly here and lock PMS. This method is not exposed
+        // to the general system server/PMS.
+        printState(writer, packageName, userId, mConnection);
+    }
+
+    @Override
+    public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+            @Nullable @UserIdInt Integer userId,
+            @Nullable Function<String, PackageSetting> pkgSettingFunction)
+            throws NameNotFoundException {
+        if (pkgSettingFunction == null) {
+            pkgSettingFunction = mConnection;
+        }
+
         synchronized (mLock) {
-            mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+            mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
         }
     }
 
@@ -920,10 +951,14 @@
      * Validates parameters provided by an external caller. Checks that an ID is still live and that
      * any provided domains are valid. Should be called at the beginning of each API that takes in a
      * {@link UUID} domain set ID.
+     *
+     * @param userIdForFilter which user to filter app access to, or null if the caller has already
+     *                        validated package visibility
      */
     @GuardedBy("mLock")
     private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
-            @NonNull Set<String> domains, boolean forAutoVerify)
+            @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
+            @Nullable Integer userIdForFilter)
             throws InvalidDomainSetException, NameNotFoundException {
         if (domainSetId == null) {
             throw new InvalidDomainSetException(null, null,
@@ -937,6 +972,13 @@
         }
 
         String pkgName = pkgState.getPackageName();
+
+        if (userIdForFilter != null
+                && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
+            throw new InvalidDomainSetException(domainSetId, null,
+                    InvalidDomainSetException.REASON_ID_INVALID);
+        }
+
         PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
         if (pkgSetting == null || pkgSetting.getPkg() == null) {
             throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
@@ -1051,6 +1093,8 @@
                 }
             }
         }
+
+        mConnection.scheduleWriteSettings();
     }
 
     /**
@@ -1108,6 +1152,8 @@
                 }
             }
         }
+
+        mConnection.scheduleWriteSettings();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
index 715d8fb..09abdd0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -23,9 +23,10 @@
 import android.util.Slog;
 
 import com.android.server.DeviceIdleInternal;
-import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 
 import java.util.Objects;
 import java.util.Set;
@@ -35,7 +36,7 @@
 
     String TAG = "DomainVerificationProxy";
 
-    boolean DEBUG_PROXIES = false;
+    boolean DEBUG_PROXIES = DomainVerificationDebug.DEBUG_PROXIES;
 
     static <ConnectionType extends DomainVerificationProxyV1.Connection
             & DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index eab89e98..9389e63 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -37,10 +37,11 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.util.Collections;
 import java.util.List;
@@ -52,7 +53,7 @@
 
     private static final String TAG = "DomainVerificationProxyV1";
 
-    private static final boolean DEBUG_BROADCASTS = false;
+    private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
 
     @NonNull
     private final Context mContext;
@@ -152,18 +153,22 @@
 
                 UUID domainSetId = pair.first;
                 String packageName = pair.second;
-                DomainVerificationInfo set;
+                DomainVerificationInfo info;
                 try {
-                    set = mManager.getDomainVerificationInfo(packageName);
+                    info = mManager.getDomainVerificationInfo(packageName);
                 } catch (PackageManager.NameNotFoundException ignored) {
                     return true;
                 }
 
-                if (!Objects.equals(domainSetId, set.getIdentifier())) {
+                if (info == null) {
                     return true;
                 }
 
-                Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
+                if (!Objects.equals(domainSetId, info.getIdentifier())) {
+                    return true;
+                }
+
+                Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet());
                 successfulDomains.removeAll(response.failedDomains);
 
                 int callingUid = response.callingUid;
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
index 9fcbce2..1ef06036 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -28,6 +28,7 @@
 import android.os.UserHandle;
 import android.util.Slog;
 
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 
 import java.util.Set;
@@ -36,7 +37,7 @@
 
     private static final String TAG = "DomainVerificationProxyV2";
 
-    private static final boolean DEBUG_BROADCASTS = true;
+    private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
 
     @NonNull
     private final Context mContext;
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index c10e828..82fc22c 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,8 +74,8 @@
         mHandler = handler;
 
         DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
-        deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context),
-                new HandlerExecutor(handler));
+        deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+                new DeviceStateListener(context));
     }
 
     void finishedGoingToSleep() {
diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java
deleted file mode 100644
index 884d7d4..0000000
--- a/services/core/java/com/android/server/policy/IconUtilities.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 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.policy;
-
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.TableMaskFilter;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.content.res.Resources;
-import android.content.Context;
-
-/**
- * Various utilities shared amongst the Launcher's classes.
- */
-public final class IconUtilities {
-
-    private int mIconWidth = -1;
-    private int mIconHeight = -1;
-    private int mIconTextureWidth = -1;
-    private int mIconTextureHeight = -1;
-
-    private final Rect mOldBounds = new Rect();
-    private final Canvas mCanvas = new Canvas();
-    private final DisplayMetrics mDisplayMetrics;
-
-    private ColorFilter mDisabledColorFilter;
-
-    public IconUtilities(Context context) {
-        final Resources resources = context.getResources();
-        DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
-        final float density = metrics.density;
-        final float blurPx = 5 * density;
-
-        mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
-        mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
-        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
-                Paint.FILTER_BITMAP_FLAG));
-    }
-
-    /**
-     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
-     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
-     */
-    public Bitmap createIconBitmap(Drawable icon) {
-        int width = mIconWidth;
-        int height = mIconHeight;
-
-        if (icon instanceof PaintDrawable) {
-            PaintDrawable painter = (PaintDrawable) icon;
-            painter.setIntrinsicWidth(width);
-            painter.setIntrinsicHeight(height);
-        } else if (icon instanceof BitmapDrawable) {
-            // Ensure the bitmap has a density.
-            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
-            Bitmap bitmap = bitmapDrawable.getBitmap();
-            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
-                bitmapDrawable.setTargetDensity(mDisplayMetrics);
-            }
-        }
-        int sourceWidth = icon.getIntrinsicWidth();
-        int sourceHeight = icon.getIntrinsicHeight();
-
-        if (sourceWidth > 0 && sourceHeight > 0) {
-            // There are intrinsic sizes.
-            if (width < sourceWidth || height < sourceHeight) {
-                // It's too big, scale it down.
-                final float ratio = (float) sourceWidth / sourceHeight;
-                if (sourceWidth > sourceHeight) {
-                    height = (int) (width / ratio);
-                } else if (sourceHeight > sourceWidth) {
-                    width = (int) (height * ratio);
-                }
-            } else if (sourceWidth < width && sourceHeight < height) {
-                // It's small, use the size they gave us.
-                width = sourceWidth;
-                height = sourceHeight;
-            }
-        }
-
-        // no intrinsic size --> use default size
-        int textureWidth = mIconTextureWidth;
-        int textureHeight = mIconTextureHeight;
-
-        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
-                Bitmap.Config.ARGB_8888);
-        final Canvas canvas = mCanvas;
-        canvas.setBitmap(bitmap);
-
-        final int left = (textureWidth-width) / 2;
-        final int top = (textureHeight-height) / 2;
-
-        mOldBounds.set(icon.getBounds());
-        icon.setBounds(left, top, left+width, top+height);
-        icon.draw(canvas);
-        icon.setBounds(mOldBounds);
-
-        return bitmap;
-    }
-
-    public ColorFilter getDisabledColorFilter() {
-        if (mDisabledColorFilter != null) {
-            return mDisabledColorFilter;
-        }
-        ColorMatrix brightnessMatrix = new ColorMatrix();
-        float brightnessF = 0.5f;
-        int brightnessI = (int) (255 * brightnessF);
-        // Brightness: C-new = C-old*(1-amount) + amount
-        float scale = 1f - brightnessF;
-        float[] mat = brightnessMatrix.getArray();
-        mat[0] = scale;
-        mat[6] = scale;
-        mat[12] = scale;
-        mat[4] = brightnessI;
-        mat[9] = brightnessI;
-        mat[14] = brightnessI;
-
-        ColorMatrix filterMatrix = new ColorMatrix();
-        filterMatrix.setSaturation(0);
-        filterMatrix.preConcat(brightnessMatrix);
-
-        mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
-        return mDisabledColorFilter;
-    }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
                 handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
             }
         });
+
         mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
                 new StateCallback() {
                     @Override
@@ -3136,7 +3138,7 @@
     private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
         final int res = applyKeyguardOcclusionChange();
         if (res != 0) return res;
-        if (keyguardGoingAway) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
             startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
         }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..db33e75 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -417,8 +417,7 @@
             WindowManagerFuncs windowManagerFuncs);
 
     /**
-     * Check permissions when adding a window or a window token from
-     * {@link android.app.WindowContext}.
+     * Check permissions when adding a window.
      *
      * @param type The window type
      * @param isRoundedCornerOverlay {@code true} to indicate the adding window is
@@ -431,7 +430,6 @@
      *      else an error code, usually
      *      {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
      *
-     * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
      * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
      */
     int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
@@ -1158,7 +1156,7 @@
      * @param startTime the start time of the animation in uptime milliseconds
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+    void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 
     /**
      * Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
 import com.android.internal.policy.IKeyguardService;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.PrintWriter;
 
@@ -398,7 +399,7 @@
     }
 
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
-        if (mKeyguardService != null) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
             mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 57e39b6..b995b19 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -45,6 +45,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.ServiceConnector;
 
+import java.lang.ref.WeakReference;
+
 
 /** Manages the connection to the remote rotation resolver service. */
 class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> {
@@ -128,13 +130,20 @@
             mProposedRotation = proposedRotation;
             mCurrentRotation = currentRotation;
             mPackageName = packageName;
-            mIRotationResolverCallback = new RotationResolverCallback();
+            mIRotationResolverCallback = new RotationResolverCallback(this);
             mCancellationSignalInternal = cancellationSignal;
             mRequestStartTimeMillis = SystemClock.elapsedRealtime();
         }
 
 
         void cancelInternal() {
+            synchronized (mLock) {
+                if (mIsFulfilled) {
+                    Slog.v(TAG, "Trying to cancel the request that has been already fulfilled.");
+                    return;
+                }
+                mIsFulfilled = true;
+            }
             Handler.getMain().post(() -> {
                 synchronized (mLock) {
                     try {
@@ -147,9 +156,6 @@
                     }
                 }
             });
-            synchronized (mLock) {
-                mIsFulfilled = true;
-            }
             mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
         }
 
@@ -160,44 +166,53 @@
             ipw.decreaseIndent();
         }
 
-        private class RotationResolverCallback extends IRotationResolverCallback.Stub {
+        private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
+            private WeakReference<RotationRequest> mRequestWeakReference;
+
+            RotationResolverCallback(RotationRequest request) {
+                this.mRequestWeakReference = new WeakReference<>(request);
+            }
+
             @Override
             public void onSuccess(int rotation) {
-                synchronized (mLock) {
-                    if (mIsFulfilled) {
+                final RotationRequest request = mRequestWeakReference.get();
+                synchronized (request.mLock) {
+                    if (request.mIsFulfilled) {
                         Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
                         return;
                     }
-                    mIsFulfilled = true;
-                    mCallbackInternal.onSuccess(rotation);
+                    request.mIsFulfilled = true;
+                    request.mCallbackInternal.onSuccess(rotation);
                     final long timeToCalculate =
-                            SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
-                    logRotationStats(mProposedRotation, mCurrentRotation, rotation,
+                            SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+                    logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
                             timeToCalculate);
                 }
             }
 
             @Override
             public void onFailure(int error) {
-                synchronized (mLock) {
-                    if (mIsFulfilled) {
+                final RotationRequest request = mRequestWeakReference.get();
+                synchronized (request.mLock) {
+                    if (request.mIsFulfilled) {
                         Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
                         return;
                     }
-                    mIsFulfilled = true;
-                    mCallbackInternal.onFailure(error);
+                    request.mIsFulfilled = true;
+                    request.mCallbackInternal.onFailure(error);
                     final long timeToCalculate =
-                            SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
-                    logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE,
-                            timeToCalculate);
+                            SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+                    logRotationStats(request.mProposedRotation, request.mCurrentRotation,
+                            RESOLUTION_FAILURE, timeToCalculate);
                 }
             }
 
             @Override
             public void onCancellable(@NonNull ICancellationSignal cancellation) {
-                synchronized (mLock) {
-                    mCancellation = cancellation;
-                    if (mCancellationSignalInternal.isCanceled()) {
+                final RotationRequest request = mRequestWeakReference.get();
+                synchronized (request.mLock) {
+                    request.mCancellation = cancellation;
+                    if (request.mCancellationSignalInternal.isCanceled()) {
                         // Dispatch the cancellation signal if the client has cancelled the request.
                         try {
                             cancellation.cancel();
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 3dbc32a..6f7c016 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -122,14 +122,9 @@
             }
         });
 
-        if (mRemoteService != null) {
-            mRemoteService.resolveRotationLocked(mCurrentRequest);
-            mCurrentRequest.mIsDispatched = true;
-        } else {
-            Slog.w(TAG, "Remote service is not available at this moment.");
-            callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
-            cancelLocked();
-        }
+
+        mRemoteService.resolveRotationLocked(mCurrentRequest);
+        mCurrentRequest.mIsDispatched = true;
     }
 
     @GuardedBy("mLock")
@@ -198,15 +193,6 @@
         if (mCurrentRequest == null) {
             return;
         }
-
-        if (mCurrentRequest.mIsFulfilled) {
-            if (isVerbose()) {
-                Slog.d(TAG, "Trying to cancel the request that has been already fulfilled.");
-            }
-            mCurrentRequest = null;
-            return;
-        }
-
         mCurrentRequest.cancelInternal();
         mCurrentRequest = null;
     }
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 4a37e79..e57d4ce 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -66,7 +66,7 @@
     private static final String KEY_SERVICE_ENABLED = "service_enabled";
 
     /** Default value in absence of {@link DeviceConfig} override. */
-    private static final boolean DEFAULT_SERVICE_ENABLED = false;
+    private static final boolean DEFAULT_SERVICE_ENABLED = true;
 
     static final int ORIENTATION_UNKNOWN =
             FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN;
@@ -150,7 +150,7 @@
         @Override
         public void resolveRotation(
                 @NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation,
-                int currentRotation, String packageName, long timeout,
+                int currentRotation, long timeout,
                 @NonNull CancellationSignal cancellationSignalInternal) {
             Objects.requireNonNull(callbackInternal);
             Objects.requireNonNull(cancellationSignalInternal);
@@ -159,7 +159,8 @@
                     final RotationResolverManagerPerUserService service = getServiceForUserLocked(
                             UserHandle.getCallingUserId());
                     service.resolveRotationLocked(callbackInternal, proposedRotation,
-                            currentRotation, packageName, timeout, cancellationSignalInternal);
+                            currentRotation, /* packageName */ "", timeout,
+                            cancellationSignalInternal);
                 } else {
                     Slog.w(TAG, "Rotation Resolver service is disabled.");
                     callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
@@ -191,7 +192,7 @@
                     TAG);
             final RotationResolverManagerPerUserService service = getServiceForUserLocked(
                     UserHandle.getCallingUserId());
-            new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback,
+            new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback,
                     resultReceiver);
         }
     }
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
similarity index 95%
rename from services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
rename to services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 0a873892..54a9edb 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -26,13 +26,13 @@
 
 import java.io.PrintWriter;
 
-final class RotationResolverShellCommend extends ShellCommand {
+final class RotationResolverShellCommand extends ShellCommand {
     private static final int INITIAL_RESULT_CODE = -1;
 
     @NonNull
     private final RotationResolverManagerPerUserService mService;
 
-    RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) {
+    RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) {
         mService = service;
     }
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 263776c..5e681c6 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2509,7 +2509,6 @@
         try {
             // force procstats to flush & combine old files into one store
             long lastHighWaterMark = readProcStatsHighWaterMark(section);
-            List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
 
             ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
             for (int i = 0; i < protoStreams.length; i++) {
@@ -2519,7 +2518,7 @@
             ProcessStats procStats = new ProcessStats(false);
             // Force processStatsService to aggregate all in-storage and in-memory data.
             long highWaterMark = processStatsService.getCommittedStatsMerged(
-                    lastHighWaterMark, section, true, statsFiles, procStats);
+                    lastHighWaterMark, section, true, null, procStats);
             procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
 
             for (int i = 0; i < protoStreams.length; i++) {
diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
new file mode 100644
index 0000000..8f22748
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tracing;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+import android.tracing.ITracingServiceProxy;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+/**
+ * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
+ * system tracing app Traceur.
+ *
+ * @hide
+ */
+public class TracingServiceProxy extends SystemService {
+    private static final String TAG = "TracingServiceProxy";
+
+    public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
+
+    private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
+    private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+
+    // Keep this in sync with the definitions in TraceService
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
+            "com.android.traceur.NOTIFY_SESSION_STOPPED";
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
+            "com.android.traceur.NOTIFY_SESSION_STOLEN";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+
+    private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
+        /**
+          * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+          * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+          * there is no buffer available to dump.
+          */
+        @Override
+        public void notifyTraceSessionEnded(boolean sessionStolen) {
+            notifyTraceur(sessionStolen);
+        }
+    };
+
+    public TracingServiceProxy(Context context) {
+        super(context);
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+    }
+
+    private void notifyTraceur(boolean sessionStolen) {
+        final Intent intent = new Intent();
+
+        try {
+            // Validate that Traceur is a system app.
+            PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME,
+                    PackageManager.MATCH_SYSTEM_ONLY);
+
+            intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
+            if (sessionStolen) {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
+            } else {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
+            }
+
+            try {
+                mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Failed to locate Traceur", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java
index f936693..44a7459 100644
--- a/services/core/java/com/android/server/utils/Watchable.java
+++ b/services/core/java/com/android/server/utils/Watchable.java
@@ -19,8 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Build;
+import android.util.Log;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 
 /**
@@ -61,40 +61,54 @@
     public void dispatchChange(@Nullable Watchable what);
 
     /**
-     * Return true if the field is tagged with @Watched
-     */
-    private static boolean isWatched(Field f) {
-        for (Annotation a : f.getDeclaredAnnotations()) {
-            if (a.annotationType().equals(Watched.class)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Verify that all @Watched {@link Watchable} attributes are being watched by this
      * class.  This requires reflection and only runs in engineering or user debug
      * builds.
+     * @param base The object that contains watched attributes.
+     * @param observer The {@link Watcher} that should be watching these attributes.
+     * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error.
      */
-    static void verifyWatchedAttributes(Object base, Watcher observer) {
-        if (Build.IS_ENG || Build.IS_USERDEBUG) {
-            for (Field f : base.getClass().getDeclaredFields()) {
+    static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) {
+        if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+            return;
+        }
+        for (Field f : base.getClass().getDeclaredFields()) {
+            if (f.getAnnotation(Watched.class) != null) {
+                final String fn = base.getClass().getName() + "." + f.getName();
                 try {
-                    final boolean flagged = isWatched(f);
+                    f.setAccessible(true);
                     final Object o = f.get(base);
-                    final boolean watchable = o instanceof Watchable;
-                    if (flagged && watchable) {
-                        Watchable attr = (Watchable) f.get(base);
+                    if (o instanceof Watchable) {
+                        Watchable attr = (Watchable) (o);
                         if (attr != null && !attr.isRegisteredObserver(observer)) {
-                            throw new RuntimeException(f.getName() + " missing an observer");
+                            if (logOnly) {
+                                Log.e("Watchable", fn + " missing an observer");
+                            } else {
+                                throw new RuntimeException("Watchable " + fn
+                                                           + " missing an observer");
+                            }
                         }
                     }
                 } catch (IllegalAccessException e) {
                     // The field is protected; ignore it.  Other exceptions that may be thrown by
                     // Field.get() are allowed to roll up.
+                    if (logOnly) {
+                        Log.e("Watchable", fn + " not visible");
+                    } else {
+                        throw new RuntimeException("Watchable " + fn + " not visible");
+                    }
                 }
             }
         }
     }
+
+    /**
+     * Verify that all @Watched {@link Watchable} attributes are being watched by this
+     * class.  This calls verifyWatchedAttributes() with logOnly set to false.
+     * @param base The object that contains watched attributes.
+     * @param observer The {@link Watcher} that should be watching these attributes.
+     */
+    static void verifyWatchedAttributes(Object base, Watcher observer) {
+        verifyWatchedAttributes(base, observer, false);
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index a82f239..3726407 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
 
 import android.annotation.NonNull;
 import android.net.NetworkCapabilities;
@@ -29,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
 import java.util.Collections;
@@ -37,6 +39,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Represents an single instance of a VCN.
@@ -82,10 +85,19 @@
     /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
     private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
 
+    /**
+     * Causes this VCN to immediately enter Safemode.
+     *
+     * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+     * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+     */
+    private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Dependencies mDeps;
     @NonNull private final VcnNetworkRequestListener mRequestListener;
+    @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
 
     @NonNull
     private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -94,14 +106,33 @@
     @NonNull private VcnConfig mConfig;
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
-    private boolean mIsRunning = true;
+    /**
+     * Whether this Vcn instance is active and running.
+     *
+     * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+     * shut down or has entered safe mode.
+     *
+     * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+     * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+     * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+     * Looper.
+     */
+    // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+    private final AtomicBoolean mIsActive = new AtomicBoolean(true);
 
     public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
-            @NonNull TelephonySubscriptionSnapshot snapshot) {
-        this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                config,
+                snapshot,
+                vcnSafemodeCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -110,10 +141,13 @@
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
             @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback,
             @NonNull Dependencies deps) {
         super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mVcnSafemodeCallback =
+                Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
         mRequestListener = new VcnNetworkRequestListener();
 
@@ -143,6 +177,11 @@
         sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
     }
 
+    /** Synchronously checks whether this Vcn is active. */
+    public boolean isActive() {
+        return mIsActive.get();
+    }
+
     /** Get current Gateways for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Set<VcnGatewayConnection> getVcnGatewayConnections() {
@@ -160,7 +199,7 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        if (!mIsRunning) {
+        if (!isActive()) {
             return;
         }
 
@@ -177,6 +216,9 @@
             case MSG_CMD_TEARDOWN:
                 handleTeardown();
                 break;
+            case MSG_CMD_ENTER_SAFEMODE:
+                handleEnterSafemode();
+                break;
             default:
                 Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
         }
@@ -184,7 +226,7 @@
 
     private void handleConfigUpdated(@NonNull VcnConfig config) {
         // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
-        Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+        Slog.v(getLogTag(), "Config updated: config = " + config.hashCode());
 
         mConfig = config;
 
@@ -198,23 +240,41 @@
             gatewayConnection.teardownAsynchronously();
         }
 
-        mIsRunning = false;
+        mIsActive.set(false);
+    }
+
+    private void handleEnterSafemode() {
+        handleTeardown();
+
+        mVcnSafemodeCallback.onEnteredSafemode();
     }
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
         if (score > getNetworkScore()) {
-            Slog.v(getLogTag(),
-                    "Request already satisfied by higher-scoring (" + score + ") network from "
-                            + "provider " + providerId + ": " + request);
+            if (VDBG) {
+                Slog.v(
+                        getLogTag(),
+                        "Request already satisfied by higher-scoring ("
+                                + score
+                                + ") network from "
+                                + "provider "
+                                + providerId
+                                + ": "
+                                + request);
+            }
             return;
         }
 
         // If preexisting VcnGatewayConnection(s) satisfy request, return
         for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
             if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                Slog.v(getLogTag(),
-                        "Request already satisfied by existing VcnGatewayConnection: " + request);
+                if (VDBG) {
+                    Slog.v(
+                            getLogTag(),
+                            "Request already satisfied by existing VcnGatewayConnection: "
+                                    + request);
+                }
                 return;
             }
         }
@@ -233,7 +293,8 @@
                                 mVcnContext,
                                 mSubscriptionGroup,
                                 mLastSnapshot,
-                                gatewayConnectionConfig);
+                                gatewayConnectionConfig,
+                                new VcnGatewayStatusCallbackImpl());
                 mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
             }
         }
@@ -242,7 +303,7 @@
     private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
         mLastSnapshot = snapshot;
 
-        if (mIsRunning) {
+        if (isActive()) {
             for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
                 gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
             }
@@ -260,7 +321,7 @@
     }
 
     private String getLogTag() {
-        return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
+        return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
     }
 
     /** Retrieves the network score for a VCN Network */
@@ -271,6 +332,20 @@
         return 52;
     }
 
+    /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public interface VcnGatewayStatusCallback {
+        /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+        @Override
+        public void onEnteredSafemode() {
+            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+        }
+    }
+
     /** External dependencies used by Vcn, for injection in tests */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class Dependencies {
@@ -279,9 +354,14 @@
                 VcnContext vcnContext,
                 ParcelUuid subscriptionGroup,
                 TelephonySubscriptionSnapshot snapshot,
-                VcnGatewayConnectionConfig connectionConfig) {
+                VcnGatewayConnectionConfig connectionConfig,
+                VcnGatewayStatusCallback gatewayStatusCallback) {
             return new VcnGatewayConnection(
-                    vcnContext, subscriptionGroup, snapshot, connectionConfig);
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    connectionConfig,
+                    gatewayStatusCallback);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 853bb43..2503e81 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,7 +61,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.State;
@@ -69,12 +68,14 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -297,9 +298,9 @@
     private static final int EVENT_SETUP_COMPLETED = 6;
 
     private static class EventSetupCompletedInfo implements EventInfo {
-        @NonNull public final ChildSessionConfiguration childSessionConfig;
+        @NonNull public final VcnChildSessionConfiguration childSessionConfig;
 
-        EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+        EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) {
             this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
         }
 
@@ -403,15 +404,13 @@
     @NonNull
     final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
 
-    @NonNull private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+    @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull private final Dependencies mDeps;
 
     @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -473,7 +472,7 @@
      * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
      * states, @Nullable otherwise.
      */
-    private ChildSessionConfiguration mChildConfig;
+    private VcnChildSessionConfiguration mChildConfig;
 
     /**
      * The active network agent.
@@ -487,8 +486,15 @@
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull TelephonySubscriptionSnapshot snapshot,
-            @NonNull VcnGatewayConnectionConfig connectionConfig) {
-        this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
+            @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                snapshot,
+                connectionConfig,
+                gatewayStatusCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -497,16 +503,17 @@
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
             @NonNull Dependencies deps) {
         super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+        mGatewayStatusCallback =
+                Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
 
-        synchronized (mLock) {
-            mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
-        }
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
 
         mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
 
@@ -577,13 +584,10 @@
      */
     public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
         Objects.requireNonNull(snapshot, "Missing snapshot");
+        mVcnContext.ensureRunningOnLooperThread();
 
-        // Vcn is the only user of this method and runs on the same Thread, but lock around
-        // mLastSnapshot to be technically correct.
-        synchronized (mLock) {
-            mLastSnapshot = snapshot;
-            mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
-        }
+        mLastSnapshot = snapshot;
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
 
         sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
     }
@@ -656,7 +660,7 @@
                 new EventTransformCreatedInfo(direction, transform));
     }
 
-    private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+    private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
         sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
     }
 
@@ -1005,7 +1009,7 @@
         protected void updateNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull NetworkAgent agent,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
@@ -1017,7 +1021,7 @@
 
         protected NetworkAgent buildNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
@@ -1065,15 +1069,15 @@
         protected void setupInterface(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             setupInterface(token, tunnelIface, childConfig, null);
         }
 
         protected void setupInterface(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig,
-                @Nullable ChildSessionConfiguration oldChildConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @Nullable VcnChildSessionConfiguration oldChildConfig) {
             try {
                 final Set<LinkAddress> newAddrs =
                         new ArraySet<>(childConfig.getInternalAddresses());
@@ -1186,7 +1190,7 @@
         protected void setupInterfaceAndNetworkAgent(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             setupInterface(token, tunnelIface, childConfig);
 
             if (mNetworkAgent == null) {
@@ -1204,7 +1208,64 @@
      */
     class RetryTimeoutState extends ActiveBaseState {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            // Reset upon entry to ConnectedState
+            mFailedAttempts++;
+
+            if (mUnderlying == null) {
+                Slog.wtf(TAG, "Underlying network was null in retry state");
+                transitionTo(mDisconnectedState);
+            } else {
+                sendMessageDelayed(
+                        EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs());
+            }
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+                    mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+                    // If new underlying is null, all networks were lost; go back to disconnected.
+                    if (mUnderlying == null) {
+                        removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                        transitionTo(mDisconnectedState);
+                        return;
+                    } else if (oldUnderlying != null
+                            && mUnderlying.network.equals(oldUnderlying.network)) {
+                        // If the network has not changed, do nothing.
+                        return;
+                    }
+
+                    // Fallthrough
+                case EVENT_RETRY_TIMEOUT_EXPIRED:
+                    removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                    transitionTo(mConnectingState);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        private long getNextRetryIntervalsMs() {
+            final int retryDelayIndex = mFailedAttempts - 1;
+            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+
+            // Repeatedly use last item in retry timeout list.
+            if (retryDelayIndex >= retryIntervalsMs.length) {
+                return retryIntervalsMs[retryIntervalsMs.length - 1];
+            }
+
+            return retryIntervalsMs[retryDelayIndex];
+        }
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1274,7 +1335,7 @@
     private static LinkProperties buildConnectedLinkProperties(
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
-            @NonNull ChildSessionConfiguration childConfig) {
+            @NonNull VcnChildSessionConfiguration childConfig) {
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1325,17 +1386,25 @@
         }
     }
 
-    private class ChildSessionCallbackImpl implements ChildSessionCallback {
+    /** Implementation of ChildSessionCallback, exposed for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public class VcnChildSessionCallback implements ChildSessionCallback {
         private final int mToken;
 
-        ChildSessionCallbackImpl(int token) {
+        VcnChildSessionCallback(int token) {
             mToken = token;
         }
 
+        /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
+        @VisibleForTesting(visibility = Visibility.PRIVATE)
+        void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
+            Slog.v(TAG, "ChildOpened for token " + mToken);
+            childOpened(mToken, childConfig);
+        }
+
         @Override
         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
-            Slog.v(TAG, "ChildOpened for token " + mToken);
-            childOpened(mToken, childConfig);
+            onOpened(new VcnChildSessionConfiguration(childConfig));
         }
 
         @Override
@@ -1418,7 +1487,7 @@
                 buildIkeParams(),
                 buildChildParams(),
                 new IkeSessionCallbackImpl(token),
-                new ChildSessionCallbackImpl(token));
+                new VcnChildSessionCallback(token));
     }
 
     /** External dependencies used by VcnGatewayConnection, for injection in tests */
@@ -1455,6 +1524,35 @@
         }
     }
 
+    /**
+     * Proxy implementation of Child Session Configuration, used for testing.
+     *
+     * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
+     * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
+     * classes) not working properly with system services & associated classes.
+     *
+     * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
+     * ChildSessionConfiguration.
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnChildSessionConfiguration {
+        private final ChildSessionConfiguration mChildConfig;
+
+        public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
+            mChildConfig = childConfig;
+        }
+
+        /** Retrieves the addresses to be used inside the tunnel. */
+        public List<LinkAddress> getInternalAddresses() {
+            return mChildConfig.getInternalAddresses();
+        }
+
+        /** Retrieves the DNS servers to be used inside the tunnel. */
+        public List<InetAddress> getInternalDnsServers() {
+            return mChildConfig.getInternalDnsServers();
+        }
+    }
+
     /** Proxy implementation of IKE session, used for testing. */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class VcnIkeSession {
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index fe4ea30..bfeec01 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.NetworkProvider;
@@ -83,11 +85,16 @@
 
     @Override
     public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
-        Slog.v(
-                TAG,
-                String.format(
-                        "Network requested: Request = %s, score = %d, providerId = %d",
-                        request, score, providerId));
+        if (VDBG) {
+            Slog.v(
+                    TAG,
+                    "Network requested: Request = "
+                            + request
+                            + ", score = "
+                            + score
+                            + ", providerId = "
+                            + providerId);
+        }
 
         final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
 
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 4f2fc86..bee66637 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -17,6 +17,7 @@
 package com.android.server.vibrator;
 
 import android.annotation.Nullable;
+import android.hardware.vibrator.IVibratorManager;
 import android.os.CombinedVibrationEffect;
 import android.os.IBinder;
 import android.os.PowerManager;
@@ -65,10 +66,13 @@
          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
          * @param vibratorIds          The id of the vibrators to be prepared.
          */
-        void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds);
+        boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
 
         /** Callback triggered after synchronized vibrations were prepared. */
-        void triggerSyncedVibration(long vibrationId);
+        boolean triggerSyncedVibration(long vibrationId);
+
+        /** Callback triggered to cancel a prepared synced vibration. */
+        void cancelSyncedVibration();
 
         /** Callback triggered when vibration thread is complete. */
         void onVibrationEnded(long vibrationId, Vibration.Status status);
@@ -146,6 +150,20 @@
         }
     }
 
+    /** Notify current vibration that a synced step has completed. */
+    public void syncedVibrationComplete() {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
+            }
+            if (mCurrentVibrateStep != null) {
+                for (int i = 0; i < mVibrators.size(); i++) {
+                    mCurrentVibrateStep.vibratorComplete(mVibrators.keyAt(i));
+                }
+            }
+        }
+    }
+
     /** Notify current vibration that a step has completed on given vibrator. */
     public void vibratorComplete(int vibratorId) {
         synchronized (mLock) {
@@ -530,7 +548,7 @@
     /** Represent a synchronized vibration step on multiple vibrators. */
     private final class SyncedVibrateStep implements VibrateStep {
         private final SparseArray<VibrationEffect> mEffects;
-        private final int mRequiredCapabilities;
+        private final long mRequiredCapabilities;
         private final int[] mVibratorIds;
 
         @GuardedBy("mLock")
@@ -539,8 +557,7 @@
         SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
             mEffects = effects;
             mActiveVibratorCounter = mEffects.size();
-            // TODO(b/159207608): Calculate required capabilities for syncing this step.
-            mRequiredCapabilities = 0;
+            mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
             mVibratorIds = new int[effects.size()];
             for (int i = 0; i < effects.size(); i++) {
                 mVibratorIds[i] = effects.keyAt(i);
@@ -574,18 +591,21 @@
         @Override
         public Vibration.Status play() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
-            long timeout = -1;
+            long duration = -1;
             try {
                 if (DEBUG) {
                     Slog.d(TAG, "SyncedVibrateStep starting...");
                 }
                 final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
                 long startTime = SystemClock.uptimeMillis();
-                mCallbacks.prepareSyncedVibration(mRequiredCapabilities, mVibratorIds);
-                timeout = startVibrating(startTime, nextSteps);
-                mCallbacks.triggerSyncedVibration(mVibration.id);
-                noteVibratorOn(timeout);
+                duration = startVibratingSynced(startTime, nextSteps);
 
+                if (duration <= 0) {
+                    // Vibrate step failed, vibrator could not be turned on for this step.
+                    return Vibration.Status.IGNORED;
+                }
+
+                noteVibratorOn(duration);
                 while (!nextSteps.isEmpty()) {
                     AmplitudeStep step = nextSteps.poll();
                     if (!waitUntil(step.startTime)) {
@@ -607,7 +627,7 @@
                 synchronized (mLock) {
                     // All OneShot and Waveform effects have finished. Just wait for the other
                     // effects to end via native callbacks before finishing this synced step.
-                    final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT;
+                    final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
                     if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
                         return Vibration.Status.FINISHED;
                     }
@@ -617,7 +637,7 @@
                     return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
                 }
             } finally {
-                if (timeout > 0) {
+                if (duration > 0) {
                     noteVibratorOff();
                 }
                 if (DEBUG) {
@@ -628,14 +648,48 @@
         }
 
         /**
+         * Starts playing effects on designated vibrators, in sync.
+         *
+         * @return A positive duration, in millis, to wait for the completion of this effect.
+         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
+         * returns the duration of a single run to be used as timeout for callbacks.
+         */
+        private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
+            // This synchronization of vibrators should be executed one at a time, even if we are
+            // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
+            // one set of vibrators at a time.
+            synchronized (mLock) {
+                boolean hasPrepared = false;
+                boolean hasTriggered = false;
+                try {
+                    hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
+                            mVibratorIds);
+                    long timeout = startVibrating(startTime, nextSteps);
+
+                    // Check if preparation was successful, otherwise devices area already vibrating
+                    if (hasPrepared) {
+                        hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
+                    }
+                    return timeout;
+                } finally {
+                    if (hasPrepared && !hasTriggered) {
+                        mCallbacks.cancelSyncedVibration();
+                        return 0;
+                    }
+                }
+            }
+        }
+
+        /**
          * Starts playing effects on designated vibrators.
          *
          * <p>This includes the {@link VibrationEffect.OneShot} and {@link VibrationEffect.Waveform}
          * effects, that should start in sync with all other effects in this step. The waveforms are
          * controlled by {@link AmplitudeStep} added to the {@code nextSteps} queue.
          *
-         * @return A duration, in millis, to wait for the completion of all vibrations. This ignores
-         * any repeating waveform duration and returns the duration of a single run.
+         * @return A positive duration, in millis, to wait for the completion of this effect.
+         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
+         * returns the duration of a single run to be used as timeout for callbacks.
          */
         private long startVibrating(long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
             long maxDuration = 0;
@@ -651,9 +705,9 @@
         /**
          * Play a single effect on a single vibrator.
          *
-         * @return A duration, in millis, to wait for the completion of this effect. This ignores
-         * any repeating waveform duration and returns the duration of a single run to be used as
-         * timeout for callbacks.
+         * @return A positive duration, in millis, to wait for the completion of this effect.
+         * Non-positive values indicate the vibrator has ignored this effect. Repeating waveform
+         * returns the duration of a single run to be used as timeout for callbacks.
          */
         private long startVibrating(VibratorController controller, VibrationEffect effect,
                 long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
@@ -698,6 +752,49 @@
                 }
             }
         }
+
+        /**
+         * Return all capabilities required from the {@link IVibratorManager} to prepare and
+         * trigger all given effects in sync.
+         *
+         * @return {@link IVibratorManager#CAP_SYNC} together with all required
+         * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
+         */
+        private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) {
+            long prepareCap = 0;
+            for (int i = 0; i < effects.size(); i++) {
+                VibrationEffect effect = effects.valueAt(i);
+                if (effect instanceof VibrationEffect.OneShot
+                        || effect instanceof VibrationEffect.Waveform) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_ON;
+                } else if (effect instanceof VibrationEffect.Prebaked) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
+                } else if (effect instanceof VibrationEffect.Composed) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
+                }
+            }
+            int triggerCap = 0;
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
+            }
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+            }
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+            }
+            return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
+        }
+
+        /**
+         * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
+         * different ones, requiring a mixed trigger capability from the vibrator manager for
+         * syncing all effects.
+         */
+        private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
+            return (prepareCapabilities & capability) != 0
+                    && (prepareCapabilities & ~capability) != 0;
+        }
     }
 
     /** Represent a step to set amplitude on a single vibrator. */
@@ -794,12 +891,12 @@
                 // Waveform has ended, no more steps to run.
                 return null;
             }
-            long nextWakeUpTime = startTime + waveform.getTimings()[currentIndex];
+            long nextStartTime = startTime + waveform.getTimings()[currentIndex];
             int nextIndex = currentIndex + 1;
             if (nextIndex >= waveform.getTimings().length) {
                 nextIndex = waveform.getRepeatIndex();
             }
-            return new AmplitudeStep(vibratorId, waveform, nextIndex, nextWakeUpTime,
+            return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
                     nextVibratorStopTime());
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3198453..5697564 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1094,7 +1094,8 @@
                     return;
                 }
                 if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
-                mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+                mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
+                        null /* options */);
                 final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                 try {
                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2ecefea..3bbc81a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,12 +16,27 @@
 
 package com.android.server.wm;
 
+import static android.os.Build.IS_USER;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
 
+import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
+import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
+import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.utils.RegionUtils.forEachRect;
@@ -29,7 +44,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
+import android.app.Application;
 import android.content.Context;
+import android.content.pm.PackageManagerInternal;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -41,15 +58,20 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.InsetsSource;
 import android.view.MagnificationSpec;
@@ -64,13 +86,22 @@
 
 import com.android.internal.R;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.TraceBuffer;
+import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -79,26 +110,37 @@
  * This class contains the accessibility related logic of the window manager.
  */
 final class AccessibilityController {
+    private static final String TAG = AccessibilityController.class.getSimpleName();
 
+    private static final Object STATIC_LOCK = new Object();
+    static AccessibilityControllerInternal
+            getAccessibilityControllerInternal(WindowManagerService service) {
+        return AccessibilityControllerInternalImpl.getInstance(service);
+    }
+
+    private final AccessibilityTracing mAccessibilityTracing;
     private final WindowManagerService mService;
-
     private static final Rect EMPTY_RECT = new Rect();
     private static final float[] sTempFloats = new float[9];
 
-    public AccessibilityController(WindowManagerService service) {
+    AccessibilityController(WindowManagerService service) {
         mService = service;
+        mAccessibilityTracing = AccessibilityTracing.getInstance(service);
     }
 
     private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
-
     private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
             new SparseArray<>();
 
     // Set to true if initializing window population complete.
     private boolean mAllObserversInitialized = true;
 
-    public boolean setMagnificationCallbacksLocked(int displayId,
-            MagnificationCallbacks callbacks) {
+    boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".setMagnificationCallbacks",
+                    "displayId=" + displayId + "; callbacks={" + callbacks + "}");
+        }
         boolean result = false;
         if (callbacks != null) {
             if (mDisplayMagnifiers.get(displayId) != null) {
@@ -118,7 +160,7 @@
             if (displayMagnifier == null) {
                 throw new IllegalStateException("Magnification callbacks already cleared!");
             }
-            displayMagnifier.destroyLocked();
+            displayMagnifier.destroy();
             mDisplayMagnifiers.remove(displayId);
             result = true;
         }
@@ -133,8 +175,13 @@
      * @param callback The callback.
      * @return {@code false} if display id is not valid or an embedded display.
      */
-    public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
+    boolean setWindowsForAccessibilityCallback(int displayId,
             WindowsForAccessibilityCallback callback) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".setWindowsForAccessibilityCallback",
+                    "displayId=" + displayId + "; callback={" + callback + "}");
+        }
         final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
         if (dc == null) {
             return false;
@@ -147,7 +194,7 @@
                 // empty, that means this mapping didn't be set, and needs to do this again.
                 // This happened when accessibility window observer is disabled and enabled again.
                 if (mWindowsForAccessibilityObserver.get(displayId) == null) {
-                    handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow());
+                    handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
                 }
                 return false;
             } else if (mWindowsForAccessibilityObserver.get(displayId) != null) {
@@ -181,7 +228,12 @@
         return true;
     }
 
-    public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) {
+    void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".performComputeChangedWindowsNot",
+                    "displayId=" + displayId + "; forceSend=" + forceSend);
+        }
         WindowsForAccessibilityObserver observer = null;
         synchronized (mService.mGlobalLock) {
             final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -191,86 +243,119 @@
             }
         }
         if (observer != null) {
-            observer.performComputeChangedWindowsNotLocked(forceSend);
+            observer.performComputeChangedWindows(forceSend);
         }
     }
 
-    public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+    void setMagnificationSpec(int displayId, MagnificationSpec spec) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+                    "displayId=" + displayId + "; spec={" + spec + "}");
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.setMagnificationSpecLocked(spec);
+            displayMagnifier.setMagnificationSpec(spec);
         }
         final WindowsForAccessibilityObserver windowsForA11yObserver =
                 mWindowsForAccessibilityObserver.get(displayId);
         if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+            windowsForA11yObserver.scheduleComputeChangedWindows();
         }
     }
 
-    public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
+    void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+                    "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+                            + "}");
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
+            displayMagnifier.getMagnificationRegion(outMagnificationRegion);
         }
     }
 
-    public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
+    void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".onRectangleOnScreenRequested",
+                    "displayId=" + displayId + "; rectangle={" + rectangle + "}");
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
+            displayMagnifier.onRectangleOnScreenRequested(rectangle);
         }
         // Not relevant for the window observer.
     }
 
-    public void onWindowLayersChangedLocked(int displayId) {
+    void onWindowLayersChanged(int displayId) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onWindowLayersChangedLocked();
+            displayMagnifier.onWindowLayersChanged();
         }
         final WindowsForAccessibilityObserver windowsForA11yObserver =
                 mWindowsForAccessibilityObserver.get(displayId);
         if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+            windowsForA11yObserver.scheduleComputeChangedWindows();
         }
     }
 
-    public void onRotationChangedLocked(DisplayContent displayContent) {
+    void onRotationChanged(DisplayContent displayContent) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+                    "displayContent={" + displayContent + "}");
+        }
         final int displayId = displayContent.getDisplayId();
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onRotationChangedLocked(displayContent);
+            displayMagnifier.onRotationChanged(displayContent);
         }
         final WindowsForAccessibilityObserver windowsForA11yObserver =
                 mWindowsForAccessibilityObserver.get(displayId);
         if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+            windowsForA11yObserver.scheduleComputeChangedWindows();
         }
     }
 
-    public void onAppWindowTransitionLocked(int displayId, int transition) {
+    void onAppWindowTransition(int displayId, int transition) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+                    "displayId=" + displayId + "; transition=" + transition);
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onAppWindowTransitionLocked(displayId, transition);
+            displayMagnifier.onAppWindowTransition(displayId, transition);
         }
         // Not relevant for the window observer.
     }
 
-    public void onWindowTransitionLocked(WindowState windowState, int transition) {
+    void onWindowTransition(WindowState windowState, int transition) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+                    "windowState={" + windowState + "}; transition=" + transition);
+        }
         final int displayId = windowState.getDisplayId();
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.onWindowTransitionLocked(windowState, transition);
+            displayMagnifier.onWindowTransition(windowState, transition);
         }
         final WindowsForAccessibilityObserver windowsForA11yObserver =
                 mWindowsForAccessibilityObserver.get(displayId);
         if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+            windowsForA11yObserver.scheduleComputeChangedWindows();
         }
     }
 
-    public void onWindowFocusChangedNotLocked(int displayId) {
+    void onWindowFocusChangedNot(int displayId) {
         // Not relevant for the display magnifier.
-
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+        }
         WindowsForAccessibilityObserver observer = null;
         synchronized (mService.mGlobalLock) {
             final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -280,7 +365,7 @@
             }
         }
         if (observer != null) {
-            observer.performComputeChangedWindowsNotLocked(false);
+            observer.performComputeChangedWindows(false);
         }
         // Since we abandon initializing observers if no window has focus, make sure all observers
         // are initialized.
@@ -311,7 +396,7 @@
         boolean areAllObserversInitialized = true;
         for (int i = unInitializedObservers.size() - 1; i >= 0; --i) {
             final  WindowsForAccessibilityObserver observer = unInitializedObservers.get(i);
-            observer.performComputeChangedWindowsNotLocked(true);
+            observer.performComputeChangedWindows(true);
             areAllObserversInitialized &= observer.mInitialized;
         }
         synchronized (mService.mGlobalLock) {
@@ -324,50 +409,89 @@
      * another display is also taken into consideration.
      * @param displayIds the display ids of displays when the situation happens.
      */
-    public void onSomeWindowResizedOrMovedLocked(int... displayIds) {
+    void onSomeWindowResizedOrMoved(int... displayIds) {
+        onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds);
+    }
+
+    void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".onSomeWindowResizedOrMoved",
+                    "displayIds={" + displayIds.toString() + "}",
+                    "".getBytes(),
+                    callingUid);
+        }
         // Not relevant for the display magnifier.
         for (int i = 0; i < displayIds.length; i++) {
             final WindowsForAccessibilityObserver windowsForA11yObserver =
                     mWindowsForAccessibilityObserver.get(displayIds[i]);
             if (windowsForA11yObserver != null) {
-                windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+                windowsForA11yObserver.scheduleComputeChangedWindows();
             }
         }
     }
 
-    public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
-            SurfaceControl.Transaction t) {
+    void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(
+                    TAG + ".drawMagnifiedRegionBorderIfNeeded",
+                    "displayId=" + displayId + "; transaction={" + t + "}");
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
+            displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
         }
         // Not relevant for the window observer.
     }
 
-    public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+    MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+                    "windowState={" + windowState + "}");
+        }
         final int displayId = windowState.getDisplayId();
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+            return displayMagnifier.getMagnificationSpecForWindow(windowState);
         }
         return null;
     }
 
-    public boolean hasCallbacksLocked() {
+    boolean hasCallbacks() {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+        }
         return (mDisplayMagnifiers.size() > 0
                 || mWindowsForAccessibilityObserver.size() > 0);
     }
 
-    public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
+    void setForceShowMagnifiableBounds(int displayId, boolean show) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
+                    "displayId=" + displayId + "; show=" + show);
+        }
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+            displayMagnifier.setForceShowMagnifiableBounds(show);
             displayMagnifier.showMagnificationBoundsIfNeeded();
         }
     }
 
-    public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId,
+    void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
             WindowState parentWindow) {
+        handleWindowObserverOfEmbeddedDisplay(
+                embeddedDisplayId, parentWindow, Binder.getCallingUid());
+    }
+
+    void handleWindowObserverOfEmbeddedDisplay(
+            int embeddedDisplayId, WindowState parentWindow, int callingUid) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+                    "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
+                    + parentWindow + "}",
+                    "".getBytes(),
+                    callingUid);
+        }
         if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
             return;
         }
@@ -390,7 +514,7 @@
         }
     }
 
-    private static void populateTransformationMatrixLocked(WindowState windowState,
+    private static void populateTransformationMatrix(WindowState windowState,
             Matrix outMatrix) {
         windowState.getTransformationMatrix(sTempFloats, outMatrix);
     }
@@ -451,6 +575,7 @@
         private final Handler mHandler;
         private final DisplayContent mDisplayContent;
         private final Display mDisplay;
+        private final AccessibilityTracing mAccessibilityTracing;
 
         private final MagnificationCallbacks mCallbacks;
 
@@ -458,7 +583,7 @@
 
         private boolean mForceShowMagnifiableBounds = false;
 
-        public DisplayMagnifier(WindowManagerService windowManagerService,
+        DisplayMagnifier(WindowManagerService windowManagerService,
                 DisplayContent displayContent,
                 Display display,
                 MagnificationCallbacks callbacks) {
@@ -469,36 +594,58 @@
             mDisplay = display;
             mHandler = new MyHandler(mService.mH.getLooper());
             mMagnifedViewport = new MagnifiedViewport();
+            mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
             mLongAnimationDuration = mDisplayContext.getResources().getInteger(
                     com.android.internal.R.integer.config_longAnimTime);
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+                        "windowManagerService={" + windowManagerService + "}; displayContent={"
+                                + displayContent + "}; display={" + display + "}; callbacks={"
+                                + callbacks + "}");
+            }
         }
 
-        public void setMagnificationSpecLocked(MagnificationSpec spec) {
-            mMagnifedViewport.updateMagnificationSpecLocked(spec);
-            mMagnifedViewport.recomputeBoundsLocked();
+        void setMagnificationSpec(MagnificationSpec spec) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+            }
+            mMagnifedViewport.updateMagnificationSpec(spec);
+            mMagnifedViewport.recomputeBounds();
 
             mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
             mService.scheduleAnimationLocked();
         }
 
-        public void setForceShowMagnifiableBoundsLocked(boolean show) {
+        void setForceShowMagnifiableBounds(boolean show) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+            }
             mForceShowMagnifiableBounds = show;
-            mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
+            mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
         }
 
-        public boolean isForceShowingMagnifiableBoundsLocked() {
+        boolean isForceShowingMagnifiableBounds() {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+            }
             return mForceShowMagnifiableBounds;
         }
 
-        public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
+        void onRectangleOnScreenRequested(Rect rectangle) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+            }
             if (DEBUG_RECTANGLE_REQUESTED) {
                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
             }
-            if (!mMagnifedViewport.isMagnifyingLocked()) {
+            if (!mMagnifedViewport.isMagnifying()) {
                 return;
             }
             Rect magnifiedRegionBounds = mTempRect2;
-            mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+            mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds);
             if (magnifiedRegionBounds.contains(rectangle)) {
                 return;
             }
@@ -511,31 +658,42 @@
                     args).sendToTarget();
         }
 
-        public void onWindowLayersChangedLocked() {
+        void onWindowLayersChanged() {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+            }
             if (DEBUG_LAYERS) {
                 Slog.i(LOG_TAG, "Layers changed.");
             }
-            mMagnifedViewport.recomputeBoundsLocked();
+            mMagnifedViewport.recomputeBounds();
             mService.scheduleAnimationLocked();
         }
 
-        public void onRotationChangedLocked(DisplayContent displayContent) {
+        void onRotationChanged(DisplayContent displayContent) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+            }
             if (DEBUG_ROTATION) {
                 final int rotation = displayContent.getRotation();
                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
                         + " displayId: " + displayContent.getDisplayId());
             }
-            mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
+            mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
             mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
         }
 
-        public void onAppWindowTransitionLocked(int displayId, int transition) {
+        void onAppWindowTransition(int displayId, int transition) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+                        "displayId=" + displayId + "; transition=" + transition);
+            }
             if (DEBUG_WINDOW_TRANSITIONS) {
                 Slog.i(LOG_TAG, "Window transition: "
                         + AppTransition.appTransitionOldToString(transition)
                         + " displayId: " + displayId);
             }
-            final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+            final boolean magnifying = mMagnifedViewport.isMagnifying();
             if (magnifying) {
                 switch (transition) {
                     case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
@@ -550,13 +708,17 @@
             }
         }
 
-        public void onWindowTransitionLocked(WindowState windowState, int transition) {
+        void onWindowTransition(WindowState windowState, int transition) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+                        "windowState={" + windowState + "}; transition=" + transition);
+            }
             if (DEBUG_WINDOW_TRANSITIONS) {
                 Slog.i(LOG_TAG, "Window transition: "
                         + AppTransition.appTransitionOldToString(transition)
                         + " displayId: " + windowState.getDisplayId());
             }
-            final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+            final boolean magnifying = mMagnifedViewport.isMagnifying();
             final int type = windowState.mAttrs.type;
             switch (transition) {
                 case WindowManagerPolicy.TRANSIT_ENTER:
@@ -586,7 +748,7 @@
                         case WindowManager.LayoutParams.TYPE_QS_DIALOG:
                         case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
                             Rect magnifiedRegionBounds = mTempRect2;
-                            mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+                            mMagnifedViewport.getMagnifiedFrameInContentCoords(
                                     magnifiedRegionBounds);
                             Rect touchableRegionBounds = mTempRect1;
                             windowState.getTouchableRegion(mTempRegion1);
@@ -604,8 +766,12 @@
             }
         }
 
-        public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
-            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+        MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
+                        "windowState={" + windowState + "}");
+            }
+            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
             if (spec != null && !spec.isNop()) {
                 if (!windowState.shouldMagnify()) {
                     return null;
@@ -614,24 +780,38 @@
             return spec;
         }
 
-        public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+        void getMagnificationRegion(Region outMagnificationRegion) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+                        "outMagnificationRegion={" + outMagnificationRegion + "}");
+            }
             // Make sure we're working with the most current bounds
-            mMagnifedViewport.recomputeBoundsLocked();
-            mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
+            mMagnifedViewport.recomputeBounds();
+            mMagnifedViewport.getMagnificationRegion(outMagnificationRegion);
         }
 
-        public void destroyLocked() {
+        void destroy() {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+            }
             mMagnifedViewport.destroyWindow();
         }
 
         // Can be called outside of a surface transaction
-        public void showMagnificationBoundsIfNeeded() {
+        void showMagnificationBoundsIfNeeded() {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+            }
             mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
                     .sendToTarget();
         }
 
-        public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
-            mMagnifedViewport.drawWindowIfNeededLocked(t);
+        void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+                        "transition={" + t + "}");
+            }
+            mMagnifedViewport.drawWindowIfNeeded(t);
         }
 
         void dump(PrintWriter pw, String prefix) {
@@ -665,7 +845,7 @@
             private boolean mFullRedrawNeeded;
             private int mTempLayer = 0;
 
-            public MagnifiedViewport() {
+            MagnifiedViewport() {
                 mBorderWidth = mDisplayContext.getResources().getDimension(
                         com.android.internal.R.dimen.accessibility_magnification_indicator_width);
                 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
@@ -681,14 +861,14 @@
                     mCircularPath = null;
                 }
 
-                recomputeBoundsLocked();
+                recomputeBounds();
             }
 
-            public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
+            void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
                 outMagnificationRegion.set(mMagnificationRegion);
             }
 
-            public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+            void updateMagnificationSpec(MagnificationSpec spec) {
                 if (spec != null) {
                     mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
                 } else {
@@ -698,12 +878,12 @@
                 // to show the border. We will do so when the pending message is handled.
                 if (!mHandler.hasMessages(
                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
-                    setMagnifiedRegionBorderShownLocked(
-                            isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
+                    setMagnifiedRegionBorderShown(
+                            isMagnifying() || isForceShowingMagnifiableBounds(), true);
                 }
             }
 
-            public void recomputeBoundsLocked() {
+            void recomputeBounds() {
                 mDisplay.getRealSize(mTempPoint);
                 final int screenWidth = mTempPoint.x;
                 final int screenHeight = mTempPoint.y;
@@ -721,7 +901,7 @@
 
                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 visibleWindows.clear();
-                populateWindowsOnScreenLocked(visibleWindows);
+                populateWindowsOnScreen(visibleWindows);
 
                 final int visibleWindowCount = visibleWindows.size();
                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
@@ -736,7 +916,7 @@
 
                     // Consider the touchable portion of the window
                     Matrix matrix = mTempMatrix;
-                    populateTransformationMatrixLocked(windowState, matrix);
+                    populateTransformationMatrix(windowState, matrix);
                     Region touchableRegion = mTempRegion3;
                     windowState.getTouchableRegion(touchableRegion);
                     Rect touchableFrame = mTempRect1;
@@ -848,24 +1028,24 @@
                 return letterboxBounds;
             }
 
-            public void onRotationChangedLocked(SurfaceControl.Transaction t) {
+            void onRotationChanged(SurfaceControl.Transaction t) {
                 // If we are showing the magnification border, hide it immediately so
                 // the user does not see strange artifacts during rotation. The screenshot
                 // used for rotation already has the border. After the rotation is complete
                 // we will show the border.
-                if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
-                    setMagnifiedRegionBorderShownLocked(false, false);
+                if (isMagnifying() || isForceShowingMagnifiableBounds()) {
+                    setMagnifiedRegionBorderShown(false, false);
                     final long delay = (long) (mLongAnimationDuration
                             * mService.getWindowAnimationScaleLocked());
                     Message message = mHandler.obtainMessage(
                             MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
                     mHandler.sendMessageDelayed(message, delay);
                 }
-                recomputeBoundsLocked();
+                recomputeBounds();
                 mWindow.updateSize(t);
             }
 
-            public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+            void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
                 if (shown) {
                     mFullRedrawNeeded = true;
                     mOldMagnificationRegion.set(0, 0, 0, 0);
@@ -873,31 +1053,31 @@
                 mWindow.setShown(shown, animate);
             }
 
-            public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+            void getMagnifiedFrameInContentCoords(Rect rect) {
                 MagnificationSpec spec = mMagnificationSpec;
                 mMagnificationRegion.getBounds(rect);
                 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
                 rect.scale(1.0f / spec.scale);
             }
 
-            public boolean isMagnifyingLocked() {
+            boolean isMagnifying() {
                 return mMagnificationSpec.scale > 1.0f;
             }
 
-            public MagnificationSpec getMagnificationSpecLocked() {
+            MagnificationSpec getMagnificationSpec() {
                 return mMagnificationSpec;
             }
 
-            public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
-                recomputeBoundsLocked();
+            void drawWindowIfNeeded(SurfaceControl.Transaction t) {
+                recomputeBounds();
                 mWindow.drawIfNeeded(t);
             }
 
-            public void destroyWindow() {
+            void destroyWindow() {
                 mWindow.releaseSurface();
             }
 
-            private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+            private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) {
                 mTempLayer = 0;
                 mDisplayContent.forAllWindows((w) -> {
                     if (w.isOnScreen() && w.isVisible()
@@ -929,7 +1109,7 @@
 
                 private boolean mInvalidated;
 
-                public ViewportWindow(Context context) {
+                ViewportWindow(Context context) {
                     SurfaceControl surfaceControl = null;
                     try {
                         mDisplay.getRealSize(mTempPoint);
@@ -971,7 +1151,7 @@
                     mInvalidated = true;
                 }
 
-                public void setShown(boolean shown, boolean animate) {
+                void setShown(boolean shown, boolean animate) {
                     synchronized (mService.mGlobalLock) {
                         if (mShown == shown) {
                             return;
@@ -986,13 +1166,13 @@
 
                 @SuppressWarnings("unused")
                 // Called reflectively from an animator.
-                public int getAlpha() {
+                int getAlpha() {
                     synchronized (mService.mGlobalLock) {
                         return mAlpha;
                     }
                 }
 
-                public void setAlpha(int alpha) {
+                void setAlpha(int alpha) {
                     synchronized (mService.mGlobalLock) {
                         if (mAlpha == alpha) {
                             return;
@@ -1005,7 +1185,7 @@
                     }
                 }
 
-                public void setBounds(Region bounds) {
+                void setBounds(Region bounds) {
                     synchronized (mService.mGlobalLock) {
                         if (mBounds.equals(bounds)) {
                             return;
@@ -1018,7 +1198,7 @@
                     }
                 }
 
-                public void updateSize(SurfaceControl.Transaction t) {
+                void updateSize(SurfaceControl.Transaction t) {
                     synchronized (mService.mGlobalLock) {
                         mDisplay.getRealSize(mTempPoint);
                         t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
@@ -1026,7 +1206,7 @@
                     }
                 }
 
-                public void invalidate(Rect dirtyRect) {
+                void invalidate(Rect dirtyRect) {
                     if (dirtyRect != null) {
                         mDirtyRect.set(dirtyRect);
                     } else {
@@ -1036,7 +1216,7 @@
                     mService.scheduleAnimationLocked();
                 }
 
-                public void drawIfNeeded(SurfaceControl.Transaction t) {
+                void drawIfNeeded(SurfaceControl.Transaction t) {
                     synchronized (mService.mGlobalLock) {
                         if (!mInvalidated) {
                             return;
@@ -1078,7 +1258,7 @@
                     }
                 }
 
-                public void releaseSurface() {
+                void releaseSurface() {
                     mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
                     mSurface.release();
                 }
@@ -1101,7 +1281,7 @@
 
                     private final ValueAnimator mShowHideFrameAnimator;
 
-                    public AnimationController(Context context, Looper looper) {
+                    AnimationController(Context context, Looper looper) {
                         super(looper);
                         mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
                                 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
@@ -1114,7 +1294,7 @@
                         mShowHideFrameAnimator.setDuration(longAnimationDuration);
                     }
 
-                    public void onFrameShownStateChanged(boolean shown, boolean animate) {
+                    void onFrameShownStateChanged(boolean shown, boolean animate) {
                         obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
                                 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
                     }
@@ -1158,7 +1338,7 @@
             public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
             public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
 
-            public MyHandler(Looper looper) {
+            MyHandler(Looper looper) {
                 super(looper);
             }
 
@@ -1193,9 +1373,9 @@
 
                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
                         synchronized (mService.mGlobalLock) {
-                            if (mMagnifedViewport.isMagnifyingLocked()
-                                    || isForceShowingMagnifiableBoundsLocked()) {
-                                mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+                            if (mMagnifedViewport.isMagnifying()
+                                    || isForceShowingMagnifiableBounds()) {
+                                mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
                                 mService.scheduleAnimationLocked();
                             }
                         }
@@ -1252,6 +1432,8 @@
 
         private final Handler mHandler;
 
+        private final AccessibilityTracing mAccessibilityTracing;
+
         private final WindowsForAccessibilityCallback mCallback;
 
         private final int mDisplayId;
@@ -1263,24 +1445,32 @@
         // Set to true if initializing window population complete.
         private boolean mInitialized;
 
-        public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+        WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                 int displayId,
                 WindowsForAccessibilityCallback callback) {
             mService = windowManagerService;
             mCallback = callback;
             mDisplayId = displayId;
             mHandler = new MyHandler(mService.mH.getLooper());
+            mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                     .getSendRecurringAccessibilityEventsInterval();
             computeChangedWindows(true);
         }
 
-        public void performComputeChangedWindowsNotLocked(boolean forceSend) {
+        void performComputeChangedWindows(boolean forceSend) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
+                        "forceSend=" + forceSend);
+            }
             mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
             computeChangedWindows(forceSend);
         }
 
-        public void scheduleComputeChangedWindowsLocked() {
+        void scheduleComputeChangedWindows() {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+            }
             if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
                         mRecurringAccessibilityEventsIntervalMillis);
@@ -1307,7 +1497,11 @@
          *
          * @param forceSend Send the windows the accessibility even if they haven't changed.
          */
-        public void computeChangedWindows(boolean forceSend) {
+        void computeChangedWindows(boolean forceSend) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+            }
             if (DEBUG) {
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
@@ -1343,7 +1537,7 @@
                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
                 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
-                populateVisibleWindowsOnScreenLocked(visibleWindows);
+                populateVisibleWindowsOnScreen(visibleWindows);
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
@@ -1518,7 +1712,7 @@
 
             // Map the frame to get what appears on the screen.
             Matrix matrix = mTempMatrix;
-            populateTransformationMatrixLocked(windowState, matrix);
+            populateTransformationMatrix(windowState, matrix);
 
             forEachRect(touchableRegion, rect -> {
                 // Move to origin as all transforms are captured by the matrix.
@@ -1563,7 +1757,7 @@
                     && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
         }
 
-        private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+        private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
             final List<WindowState> tempWindowStatesList = new ArrayList<>();
             final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
             if (dc == null) {
@@ -1637,4 +1831,292 @@
             }
         }
     }
+
+    private static final class AccessibilityControllerInternalImpl
+            implements AccessibilityControllerInternal {
+
+        private static AccessibilityControllerInternal sInstance;
+        static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+            synchronized (STATIC_LOCK) {
+                if (sInstance == null) {
+                    sInstance = new AccessibilityControllerInternalImpl(service);
+                }
+                return sInstance;
+            }
+        }
+
+        private final AccessibilityTracing mTracing;
+        private AccessibilityControllerInternalImpl(WindowManagerService service) {
+            mTracing = AccessibilityTracing.getInstance(service);
+        }
+
+        @Override
+        public void startTrace() {
+            mTracing.startTrace();
+        }
+
+        @Override
+        public void stopTrace() {
+            mTracing.stopTrace();
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return mTracing.isEnabled();
+        }
+
+        @Override
+        public void logTrace(
+                String where, String callingParams, byte[] a11yDump, int callingUid,
+                StackTraceElement[] stackTrace) {
+            mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+        }
+    }
+
+    private static final class AccessibilityTracing {
+        private static AccessibilityTracing sInstance;
+        static AccessibilityTracing getInstance(WindowManagerService service) {
+            synchronized (STATIC_LOCK) {
+                if (sInstance == null) {
+                    sInstance = new AccessibilityTracing(service);
+                }
+                return sInstance;
+            }
+        }
+
+        private static final int BUFFER_CAPACITY = 4096 * 1024;
+        private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
+        private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
+        private static final String TAG = "AccessibilityTracing";
+        private static final long MAGIC_NUMBER_VALUE =
+                ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+        private final Object mLock = new Object();
+        private final WindowManagerService mService;
+        private final File mTraceFile;
+        private final TraceBuffer mBuffer;
+        private final LogHandler mHandler;
+        private volatile boolean mEnabled;
+
+        AccessibilityTracing(WindowManagerService service) {
+            mService = service;
+            mTraceFile = new File(TRACE_FILENAME);
+            mBuffer = new TraceBuffer(BUFFER_CAPACITY);
+            HandlerThread workThread = new HandlerThread(TAG);
+            workThread.start();
+            mHandler = new LogHandler(workThread.getLooper());
+        }
+
+        /**
+         * Start the trace.
+         */
+        void startTrace() {
+            if (IS_USER) {
+                Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+                return;
+            }
+            synchronized (mLock) {
+                try {
+                    Files.createDirectories(Paths.get(TRACE_DIRECTORY));
+                    mTraceFile.createNewFile();
+                } catch (Exception e) {
+                    Slog.e(TAG, "Error: Failed to create trace file.");
+                    return;
+                }
+                mEnabled = true;
+                mBuffer.resetBuffer();
+            }
+        }
+
+        /**
+         * Stops the trace and write the current buffer to disk
+         */
+        void stopTrace() {
+            if (IS_USER) {
+                Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+                return;
+            }
+            synchronized (mLock) {
+                mEnabled = false;
+                if (mEnabled) {
+                    Slog.e(TAG, "Error: tracing enabled while waiting for flush.");
+                    return;
+                }
+                writeTraceToFile();
+            }
+        }
+
+        boolean isEnabled() {
+            return mEnabled;
+        }
+
+        /**
+         * Write an accessibility trace log entry.
+         */
+        void logState(String where) {
+            if (!mEnabled) {
+                return;
+            }
+            logState(where, "");
+        }
+
+        /**
+         * Write an accessibility trace log entry.
+         */
+        void logState(String where, String callingParams) {
+            if (!mEnabled) {
+                return;
+            }
+            logState(where, callingParams, "".getBytes());
+        }
+
+        /**
+         * Write an accessibility trace log entry.
+         */
+        void logState(String where, String callingParams, byte[] a11yDump) {
+            if (!mEnabled) {
+                return;
+            }
+            logState(where, callingParams, a11yDump, Binder.getCallingUid());
+        }
+
+        /**
+         * Write an accessibility trace log entry.
+         */
+        void logState(
+                String where, String callingParams, byte[] a11yDump, int callingUid) {
+            if (!mEnabled) {
+                return;
+            }
+            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+
+            logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+        }
+
+        /**
+         * Write an accessibility trace log entry.
+         */
+        void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
+                StackTraceElement[] stackTrace) {
+            if (!mEnabled) {
+                return;
+            }
+
+            log(where, callingParams, a11yDump, callingUid, stackTrace);
+        }
+
+        private  String toStackTraceString(StackTraceElement[] stackTraceElements) {
+            if (stackTraceElements == null) {
+                return "";
+            }
+            StringBuilder stringBuilder = new StringBuilder();
+            boolean skip = true;
+            for (int i = 0; i < stackTraceElements.length; i++) {
+                if (stackTraceElements[i].toString().contains(
+                            AccessibilityTracing.class.getSimpleName())) {
+                    skip = false;
+                } else if (!skip) {
+                    stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+                }
+            }
+            return stringBuilder.toString();
+        }
+
+        /**
+         * Write the current state to the buffer
+         */
+        private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
+                StackTraceElement[] stackTrace) {
+            SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = SystemClock.elapsedRealtimeNanos();
+            args.arg2 = fm.format(new Date()).toString();
+            args.arg3 = where;
+            args.arg4 = Process.myPid() + ":" + Application.getProcessName();
+            args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName();
+            args.arg6 = callingUid;
+            args.arg7 = callingParams;
+            args.arg8 = stackTrace;
+            args.arg9 = a11yDump;
+            mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+        }
+
+        /**
+         * Writes the trace buffer to new file for the bugreport.
+         */
+        void writeTraceToFile() {
+            mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE);
+        }
+
+        private class LogHandler extends Handler {
+            public static final int MESSAGE_LOG_TRACE_ENTRY = 1;
+            public static final int MESSAGE_WRITE_FILE = 2;
+
+            LogHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MESSAGE_LOG_TRACE_ENTRY: {
+                        final SomeArgs args = (SomeArgs) message.obj;
+                        try {
+                            ProtoOutputStream os = new ProtoOutputStream();
+                            PackageManagerInternal pmInternal =
+                                    LocalServices.getService(PackageManagerInternal.class);
+
+                            long tokenOuter = os.start(ENTRY);
+                            String callingStack =
+                                    toStackTraceString((StackTraceElement[]) args.arg8);
+
+                            os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1);
+                            os.write(CALENDAR_TIME, (String) args.arg2);
+                            os.write(WHERE, (String) args.arg3);
+                            os.write(PROCESS_NAME, (String) args.arg4);
+                            os.write(THREAD_ID_NAME, (String) args.arg5);
+                            os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6));
+                            os.write(CALLING_PARAMS, (String) args.arg7);
+                            os.write(CALLING_STACKS, callingStack);
+                            os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
+
+                            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+                            synchronized (mService.mGlobalLock) {
+                                mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
+                            }
+                            os.end(tokenInner);
+
+                            os.end(tokenOuter);
+                            synchronized (mLock) {
+                                mBuffer.add(os);
+                            }
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Exception while tracing state", e);
+                        }
+                        break;
+                    }
+                    case MESSAGE_WRITE_FILE: {
+                        synchronized (mLock) {
+                            writeTraceToFileInternal();
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Writes the trace buffer to disk.
+         */
+        private void writeTraceToFileInternal() {
+            try {
+                ProtoOutputStream proto = new ProtoOutputStream();
+                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+                mBuffer.writeTraceToFile(mTraceFile, proto);
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to write buffer to file", e);
+            }
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 6bca484..3a0eb39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -978,6 +978,7 @@
         final TransitionInfoSnapshot infoSnapshot =
                 new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+        mLastTransitionInfo.remove(r);
 
         if (!info.isInterestingToLoggerAndObserver()) {
             return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 81baaa2..68a2c5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
 import android.service.dreams.DreamActivity;
 import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
@@ -1366,7 +1362,6 @@
             return;
         }
         final boolean surfaceReady = w.isDrawn()  // Regular case
-                || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
                 || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
         final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
         updateRoundedCorners(w);
@@ -6533,7 +6528,7 @@
         // Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
         // task being started in the wrong orientation during the transition.
         if (!getDisplayContent().mClosingApps.contains(this)
-                && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
+                && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
             return mOrientation;
         }
 
@@ -6779,20 +6774,6 @@
         // layout traversals.
         mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
         getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
-        // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
-        // size compat mode.
-        if (providesMaxBounds()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
-                        + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
-                        + "mode %s", getUid(),
-                        resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
-                        !matchParentBounds(), inSizeCompatMode());
-            }
-            resolvedConfig.windowConfiguration
-                    .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
-        }
     }
 
     /**
@@ -6976,19 +6957,6 @@
         return super.getBounds();
     }
 
-    @Override
-    public boolean providesMaxBounds() {
-        // System and SystemUI should always be able to access the physical display bounds,
-        // so do not provide it with the overridden maximum bounds.
-        // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
-        if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
-                getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
-            return false;
-        }
-        // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
-        return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
-    }
-
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appRootTaskClipMode) {
@@ -7037,7 +7005,32 @@
                 return;
             }
         }
-        super.onConfigurationChanged(newParentConfig);
+
+        final DisplayContent display = mDisplayContent;
+        if (inPinnedWindowingMode() && attachedToProcess() && display != null) {
+            // If the PIP activity is changing to fullscreen with display orientation change, the
+            // fixed rotation will take effect that requires to send fixed rotation adjustments
+            // before the process configuration (if the process is a configuration listener of the
+            // activity). So when performing process configuration on client side, it can apply
+            // the adjustments (see WindowToken#onFixedRotationStatePrepared).
+            try {
+                app.pauseConfigurationDispatch();
+                super.onConfigurationChanged(newParentConfig);
+                if (mVisibleRequested && !inMultiWindowMode()) {
+                    final int rotation = display.rotationForActivityInDifferentOrientation(this);
+                    if (rotation != ROTATION_UNDEFINED) {
+                        app.resumeConfigurationDispatch();
+                        display.setFixedRotationLaunchingApp(this, rotation);
+                    }
+                }
+            } finally {
+                if (app.resumeConfigurationDispatch()) {
+                    app.dispatchConfiguration(app.getConfiguration());
+                }
+            }
+        } else {
+            super.onConfigurationChanged(newParentConfig);
+        }
 
         // Configuration's equality doesn't consider seq so if only seq number changes in resolved
         // override configuration. Therefore ConfigurationContainer doesn't change merged override
@@ -7046,7 +7039,6 @@
             onMergedOverrideConfigurationChanged();
         }
 
-        final DisplayContent display = mDisplayContent;
         if (display == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 79f8229..3456e51 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -772,6 +772,11 @@
             newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
         }
         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target));
+        ActivityOptions options = mRequest.activityOptions.getOptions(mRequest.intent,
+                mRequest.activityInfo,
+                mService.getProcessController(mRequest.caller),
+                mSupervisor);
+        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_ACTIVITY_OPTIONS, options.toBundle());
         heavy.updateIntentForHeavyWeightActivity(newIntent);
         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                 mRequest.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2d6e9b2..f16a646 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -130,6 +130,7 @@
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
+import android.app.AnrController;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.Dialog;
@@ -175,6 +176,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.power.Mode;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -342,7 +344,7 @@
     private StatusBarManagerInternal mStatusBarManagerInternal;
     @VisibleForTesting
     final ActivityTaskManagerInternal mInternal;
-    PowerManagerInternal mPowerManagerInternal;
+    private PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
     PendingIntentController mPendingIntentController;
@@ -494,6 +496,7 @@
      */
     private volatile long mLastStopAppSwitchesTime;
 
+    private final List<AnrController> mAnrController = new ArrayList<>();
     IActivityController mController = null;
     boolean mControllerIsAMonkey = false;
 
@@ -589,6 +592,22 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+            POWER_MODE_REASON_START_ACTIVITY,
+            POWER_MODE_REASON_FREEZE_DISPLAY,
+            POWER_MODE_REASON_ALL,
+    })
+    @interface PowerModeReason {}
+
+    static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
+    static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+    /** This can only be used by {@link #endLaunchPowerMode(int)}.*/
+    static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
+
+    /** The reasons to use {@link Mode#LAUNCH} power mode. */
+    private @PowerModeReason int mLaunchPowerModeReasons;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
             LAYOUT_REASON_CONFIG_CHANGED,
             LAYOUT_REASON_VISIBILITY_CHANGED,
     })
@@ -769,7 +788,7 @@
         final boolean sizeCompatFreeform = Settings.Global.getInt(
                 resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
         final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(
-                resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+                resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
 
         // Transfer any global setting for forcing RTL layout, into a System Property
         DisplayProperties.debug_force_rtl(forceRtl);
@@ -2058,6 +2077,40 @@
         return mAppSwitchesAllowed;
     }
 
+    /** Register an {@link AnrController} to control the ANR dialog behavior */
+    public void registerAnrController(AnrController controller) {
+        synchronized (mGlobalLock) {
+            mAnrController.add(controller);
+        }
+    }
+
+    /** Unregister an {@link AnrController} */
+    public void unregisterAnrController(AnrController controller) {
+        synchronized (mGlobalLock) {
+            mAnrController.remove(controller);
+        }
+    }
+
+    /** @return the max ANR delay from all registered {@link AnrController} instances */
+    public long getMaxAnrDelayMillis(ApplicationInfo info) {
+        if (info == null || info.packageName == null) {
+            return 0;
+        }
+
+        final ArrayList<AnrController> controllers;
+        synchronized (mGlobalLock) {
+            controllers = new ArrayList<>(mAnrController);
+        }
+
+        final String packageName = info.packageName;
+        long maxDelayMs = 0;
+        for (AnrController controller : controllers) {
+            maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid));
+        }
+        maxDelayMs = Math.max(maxDelayMs, 0);
+        return maxDelayMs;
+    }
+
     @Override
     public void setActivityController(IActivityController controller, boolean imAMonkey) {
         mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
@@ -4059,6 +4112,20 @@
         return changes;
     }
 
+    void startLaunchPowerMode(@PowerModeReason int reason) {
+        if (mPowerManagerInternal == null) return;
+        mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+        mLaunchPowerModeReasons |= reason;
+    }
+
+    void endLaunchPowerMode(@PowerModeReason int reason) {
+        if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+        mLaunchPowerModeReasons &= ~reason;
+        if (mLaunchPowerModeReasons == 0) {
+            mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
+        }
+    }
+
     /** @see WindowSurfacePlacer#deferLayout */
     void deferWindowLayout() {
         if (!mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index de43643..1264d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1755,7 +1755,7 @@
         }
 
         // End power mode launch before going sleep
-        mRootWindowContainer.endPowerModeLaunchIfNeeded();
+        mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
 
         removeSleepTimeouts();
 
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index fde0369..c589fea 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -39,7 +39,7 @@
 import android.os.Bundle;
 
 import com.android.internal.R;
-import com.android.server.policy.IconUtilities;
+import com.android.internal.util.ImageUtils;
 
 /** Displays an ongoing notification for a process displaying an alert window */
 class AlertWindowNotification {
@@ -54,7 +54,6 @@
     private final NotificationManager mNotificationManager;
     private final String mPackageName;
     private boolean mPosted;
-    private IconUtilities mIconUtilities;
 
     AlertWindowNotification(WindowManagerService service, String packageName) {
         mService = service;
@@ -63,7 +62,6 @@
                 (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
         mNotificationTag = CHANNEL_PREFIX + mPackageName;
         mRequestCode = sNextRequestCode++;
-        mIconUtilities = new IconUtilities(mService.mContext);
     }
 
     void post() {
@@ -126,8 +124,9 @@
 
         if (aInfo != null) {
             final Drawable drawable = pm.getApplicationIcon(aInfo);
-            if (drawable != null) {
-                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+            int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+            final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
+            if (bitmap != null) {
                 builder.setLargeIcon(bitmap);
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index eba3f93..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
@@ -257,6 +258,7 @@
     private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
 
     private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+    private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
     private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
 
     private int mLastClipRevealMaxTranslation;
@@ -446,7 +448,7 @@
                 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
 
         if (mRemoteAnimationController != null) {
-            mRemoteAnimationController.goodToGo();
+            mRemoteAnimationController.goodToGo(transit);
         }
         return redoLayout;
     }
@@ -509,6 +511,11 @@
         mListeners.remove(listener);
     }
 
+    void registerKeygaurdExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener) {
+        mKeyguardExitAnimationStartListener = listener;
+    }
+
     public void notifyAppTransitionFinishedLocked(IBinder token) {
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onAppTransitionFinishedLocked(token);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6e8257b..9ca7eca 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
     private final DisplayContent mDisplayContent;
     private final WallpaperController mWallpaperControllerLocked;
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+    private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
 
     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
 
@@ -437,10 +438,14 @@
                 return adapter;
             }
         }
-        if (mRemoteAnimationDefinition == null) {
-            return null;
+        if (mRemoteAnimationDefinition != null) {
+            final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+                    transit, activityTypes);
+            if (adapter != null) {
+                return adapter;
+            }
         }
-        return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+        return null;
     }
 
     /**
@@ -719,8 +724,7 @@
         final AccessibilityController accessibilityController =
                 mDisplayContent.mWmService.mAccessibilityController;
         if (accessibilityController != null) {
-            accessibilityController.onAppWindowTransitionLocked(
-                    mDisplayContent.getDisplayId(), transit);
+            accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3ab7952..23eab98 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -685,6 +685,10 @@
      */
     private boolean mInEnsureActivitiesVisible = false;
 
+    // Used to indicate that the movement of child tasks to top will not move the display to top as
+    // well and thus won't change the top resumed / focused record
+    boolean mDontMoveToTop;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -948,7 +952,7 @@
         }
 
         final ActivityRecord activity = w.mActivityRecord;
-        if (activity != null) {
+        if (activity != null && activity.isVisibleRequested()) {
             activity.updateLetterboxSurface(w);
             final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
             if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
@@ -1226,7 +1230,7 @@
 
         if (mWmService.mAccessibilityController != null) {
             final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY;
-            mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(prevDisplayId,
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId,
                     getDisplayId());
         }
     }
@@ -1447,7 +1451,9 @@
             }
             config = new Configuration();
             computeScreenConfiguration(config);
-        } else if (currentConfig != null) {
+        } else if (currentConfig != null
+                // If waiting for a remote rotation, don't prematurely update configuration.
+                && !mDisplayRotation.isWaitingForRemoteRotation()) {
             // No obvious action we need to take, but if our current state mismatches the
             // activity manager's, update it, disregarding font scale, which should remain set
             // to the value of the previous configuration.
@@ -1597,7 +1603,6 @@
                 && mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
     }
 
-    @VisibleForTesting
     boolean isFixedRotationLaunchingApp(ActivityRecord r) {
         return mFixedRotationLaunchingApp == r;
     }
@@ -1826,8 +1831,6 @@
             if (w.mHasSurface && !rotateSeamlessly) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
                 w.setOrientationChanging(true);
-                mWmService.mRoot.mOrientationChangeComplete = false;
-                w.mLastFreezeDuration = 0;
             }
             w.mReportOrientationChanged = true;
         }, true /* traverseTopToBottom */);
@@ -1850,7 +1853,7 @@
         }
 
         if (mWmService.mAccessibilityController != null) {
-            mWmService.mAccessibilityController.onRotationChangedLocked(this);
+            mWmService.mAccessibilityController.onRotationChanged(this);
         }
     }
 
@@ -3400,7 +3403,7 @@
     }
 
     void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) {
-        accessibilityController.onWindowFocusChangedNotLocked(getDisplayId());
+        accessibilityController.onWindowFocusChangedNot(getDisplayId());
     }
 
     private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
@@ -4963,7 +4966,7 @@
         if (!mLocationInParentWindow.equals(x, y)) {
             mLocationInParentWindow.set(x, y);
             if (mWmService.mAccessibilityController != null) {
-                mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId);
+                mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId);
             }
             notifyLocationInParentDisplayChanged();
         }
@@ -5960,36 +5963,6 @@
         }
     }
 
-    /**
-     * Returns the number of window tokens without surface on this display. A {@link WindowToken}
-     * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}.
-     * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and
-     * limit the usage if the count exceeds a number.
-     *
-     * @param callingUid app calling uid
-     * @return the number of window tokens without surface on this display
-     * @see WindowToken#addWindow(WindowState)
-     */
-    int getWindowTokensWithoutSurfaceCount(int callingUid) {
-        List<WindowToken> tokens = new ArrayList<>(mTokenMap.values());
-        int count = 0;
-        for (int i = tokens.size() - 1; i >= 0; i--) {
-            final WindowToken token = tokens.get(i);
-            if (callingUid != token.getOwnerUid()) {
-                continue;
-            }
-            // Skip if token is an Activity
-            if (token.asActivityRecord() != null) {
-                continue;
-            }
-            if (token.mSurfaceControl != null) {
-                continue;
-            }
-            count++;
-        }
-        return count;
-    }
-
     MagnificationSpec getMagnificationSpec() {
         return mMagnificationSpec;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 61fe023..5460e36 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2086,6 +2086,7 @@
                 pi.getResDir(),
                 null /* splitResDirs */,
                 pi.getOverlayDirs(),
+                pi.getOverlayPaths(),
                 pi.getApplicationInfo().sharedLibraryFiles,
                 mDisplayContent.getDisplayId(),
                 null /* overrideConfig */,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 48e4df7..b106657 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -481,12 +481,12 @@
 
         mRotation = rotation;
 
+        mDisplayContent.setLayoutNeeded();
+
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
         mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
                 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
 
-        mDisplayContent.setLayoutNeeded();
-
         if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
             // The screen rotation animation uses a screenshot to freeze the screen while windows
             // resize underneath. When we are rotating seamlessly, we allow the elements to
@@ -1499,6 +1499,21 @@
         }
 
         @Override
+        public boolean canUseRotationResolver() {
+            if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+            switch (mCurrentAppOrientation) {
+                case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+                case ActivityInfo.SCREEN_ORIENTATION_USER:
+                case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+                case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+                case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                    return true;
+            }
+            return false;
+        }
+
+        @Override
         public void onProposedRotationChanged(int rotation) {
             ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
             Runnable r = mRunnableCache.get(rotation, null);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index b82fdd2..6d5abe1 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,11 +16,11 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
@@ -196,6 +196,14 @@
         mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
     }
 
+    void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) {
+        DisplayInfo displayInfo = dc.getDisplayInfo();
+        SettingsProvider.SettingsEntry overrideSettings =
+                mSettingsProvider.getSettings(displayInfo);
+        overrideSettings.mDontMoveToTop = dontMoveToTop;
+        mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+    }
+
     boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
         if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
             // Default display should show system decors.
@@ -274,6 +282,10 @@
         final int forcedScalingMode = settings.mForcedScalingMode != null
                 ? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO;
         dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED;
+
+        boolean dontMoveToTop = settings.mDontMoveToTop != null
+                ? settings.mDontMoveToTop : false;
+        dc.mDontMoveToTop = dontMoveToTop;
     }
 
     /**
@@ -358,6 +370,8 @@
             Boolean mIgnoreOrientationRequest;
             @Nullable
             Boolean mIgnoreDisplayCutout;
+            @Nullable
+            Boolean mDontMoveToTop;
 
             SettingsEntry() {}
 
@@ -432,6 +446,10 @@
                     mIgnoreDisplayCutout = other.mIgnoreDisplayCutout;
                     changed = true;
                 }
+                if (other.mDontMoveToTop != mDontMoveToTop) {
+                    mDontMoveToTop = other.mDontMoveToTop;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -515,6 +533,11 @@
                     mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout;
                     changed = true;
                 }
+                if (delta.mDontMoveToTop != null
+                        && delta.mDontMoveToTop != mDontMoveToTop) {
+                    mDontMoveToTop = delta.mDontMoveToTop;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -531,7 +554,8 @@
                         && mImePolicy == null
                         && mFixedToUserRotation == null
                         && mIgnoreOrientationRequest == null
-                        && mIgnoreDisplayCutout == null;
+                        && mIgnoreDisplayCutout == null
+                        && mDontMoveToTop == null;
             }
 
             @Override
@@ -553,7 +577,8 @@
                         && Objects.equals(mImePolicy, that.mImePolicy)
                         && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
                         && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
-                        && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout);
+                        && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
+                        && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
             }
 
             @Override
@@ -561,7 +586,8 @@
                 return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
                         mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
                         mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
-                        mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout);
+                        mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
+                        mDontMoveToTop);
             }
 
             @Override
@@ -581,6 +607,7 @@
                         + ", mFixedToUserRotation=" + mFixedToUserRotation
                         + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
                         + ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
+                        + ", mDontMoveToTop=" + mDontMoveToTop
                         + '}';
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 5f3ab43..8fcdf2e 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -109,12 +109,13 @@
      */
     void setBaseSettingsFilePath(@Nullable String path) {
         AtomicFile settingsFile;
-        if (path != null) {
-            settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG);
+        File file = path != null ? new File(path) : null;
+        if (file != null && file.exists()) {
+            settingsFile = new AtomicFile(file, WM_DISPLAY_COMMIT_TAG);
         } else {
+            Slog.w(TAG, "display settings " + path + " does not exist, using vendor defaults");
             settingsFile = getVendorSettingsFile();
         }
-
         setBaseSettingsStorage(new AtomicFileStorage(settingsFile));
     }
 
@@ -405,6 +406,9 @@
                     "ignoreOrientationRequest", null /* defaultValue */);
             settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
                     "ignoreDisplayCutout", null /* defaultValue */);
+            settingsEntry.mDontMoveToTop = getBooleanAttribute(parser,
+                    "dontMoveToTop", null /* defaultValue */);
+
             fileData.mSettings.put(name, settingsEntry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -496,6 +500,10 @@
                     out.attributeBoolean(null, "ignoreDisplayCutout",
                             settingsEntry.mIgnoreDisplayCutout);
                 }
+                if (settingsEntry.mDontMoveToTop != null) {
+                    out.attributeBoolean(null, "dontMoveToTop",
+                            settingsEntry.mDontMoveToTop);
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 803bec8..de4bdaa 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -47,7 +47,7 @@
         mTouchRegion.set(touchRegion);
         // We need to report touchable region changes to accessibility.
         if (mDisplayContent.mWmService.mAccessibilityController != null) {
-            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
                     mDisplayContent.getDisplayId());
         }
     }
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
         }
 
         public void applySurfaceChanges(SurfaceControl.Transaction t) {
-            if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+            if (!needsApplySurfaceChanges()) {
                 // Nothing changed.
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 26f0f09..4f8ea1a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -284,7 +284,8 @@
             // Just to be sure end the launch hint in case the target activity was never launched.
             // However, if we're keeping the activity and making it visible, we can leave it on.
             if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mService.mRootWindowContainer.endPowerModeLaunchIfNeeded();
+                mService.endLaunchPowerMode(
+                        ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
             }
 
             // Once the target is shown, prevent spurious background app switches
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
 
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
     /**
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
-    void goodToGo() {
+    void goodToGo(@WindowManager.TransitionOldType int transit) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
         if (mPendingAnimations.isEmpty() || mCanceled) {
             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
 
         // Create the remote wallpaper animation targets (if any)
         final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+        // TODO(bc-unlock): Create the remote non app animation targets (if any)
+        final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
                 linkToDeathOfRunner();
-                mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
-                        mFinishedCallback);
+                mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+                        wallpaperTargets, nonAppTargets, mFinishedCallback);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to start remote animation", e);
                 onAnimationFinished();
@@ -274,6 +279,7 @@
     private void setRunningRemoteAnimation(boolean running) {
         final int pid = mRemoteAnimationAdapter.getCallingPid();
         final int uid = mRemoteAnimationAdapter.getCallingUid();
+
         if (pid == 0) {
             throw new RuntimeException("Calling pid of remote animation was null");
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 61fec0d..bd93e04 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -89,7 +89,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 
@@ -267,9 +266,6 @@
      */
     final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
 
-    /** Set when a power mode launch has started, but not ended. */
-    private boolean mPowerModeLaunchStarted;
-
     // The default minimal size that will be used if the activity doesn't specify its minimal size.
     // It will be calculated when the default display gets added.
     int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -847,12 +843,12 @@
             Slog.i(TAG,
                     ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
         }
-        // Send any pending task-info changes that were queued-up during a layout deferment
-        mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
         mWmService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction();
+            // Send any pending task-info changes that were queued-up during a layout deferment
+            mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
             mWmService.mSyncEngine.onSurfacePlacement();
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
@@ -933,7 +929,6 @@
                     displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 }
                 win.destroySurfaceUnchecked();
-                win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction());
             } while (i > 0);
             mWmService.mDestroySurface.clear();
         }
@@ -1185,10 +1180,7 @@
             mUpdateRotation = true;
             doRequest = true;
         }
-        if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
-            mOrientationChangeComplete = false;
-        } else {
-            mOrientationChangeComplete = true;
+        if (mOrientationChangeComplete) {
             mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
             if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                 doRequest = true;
@@ -3328,7 +3320,7 @@
             }
         }
         // End power mode launch when idle.
-        endPowerModeLaunchIfNeeded();
+        mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
         return true;
     }
 
@@ -3541,17 +3533,9 @@
             sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
         }
 
-        if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
-            mPowerModeLaunchStarted = true;
-        }
-    }
-
-    void endPowerModeLaunchIfNeeded() {
-        // Trigger launch power mode off if activity is launched
-        if (mPowerModeLaunchStarted && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
-            mPowerModeLaunchStarted = false;
+        if (sendPowerModeLaunch) {
+            mService.startLaunchPowerMode(
+                    ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f8daf6..8b18679 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -104,9 +104,6 @@
     final boolean mCanAddInternalSystemWindow;
     private final boolean mCanStartTasksFromRecents;
 
-    // If non-system overlays from this process can be hidden by the user or app using
-    // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
-    final boolean mOverlaysCanBeHidden;
     final boolean mCanCreateSystemApplicationOverlay;
     final boolean mCanHideNonSystemOverlayWindows;
     final boolean mCanAcquireSleepToken;
@@ -136,8 +133,6 @@
                         == PERMISSION_GRANTED;
         mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
                 START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
-        mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
-                && !mService.mAtmInternal.isCallerRecents(mUid);
         mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
                 == PERMISSION_GRANTED;
         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -253,11 +248,6 @@
     }
 
     @Override
-    public void setTransparentRegion(IWindow window, Region region) {
-        mService.setTransparentRegionWindow(this, window, region);
-    }
-
-    @Override
     public void setInsets(IWindow window, int touchableInsets,
             Rect contentInsets, Rect visibleInsets, Region touchableArea) {
         mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
@@ -682,7 +672,7 @@
 
         boolean changed;
 
-        if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) {
+        if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
             // We want to track non-system apps adding alert windows so we can post an
             // on-going notification for the user to control their visibility.
             if (visible) {
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 62c0527..0902948 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -191,7 +191,7 @@
             }
         }
         if (mDisplayContent.mWmService.mAccessibilityController != null) {
-            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
                     mDisplayContent.getDisplayId());
         }
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30af0ed..9bbbbe0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -812,6 +810,16 @@
     private boolean mDeferTaskAppear;
 
     /**
+     * Forces this task to be unorganized. Currently it is used for deferring the control of
+     * organizer when windowing mode is changing from PiP to fullscreen with orientation change.
+     * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
+     *
+     * TODO(b/179235349): Remove this field by making surface operations from task organizer sync
+     *                    with display rotation.
+     */
+    private boolean mForceNotOrganized;
+
+    /**
      * This task was created by the task organizer which has the following implementations.
      * <ul>
      *     <lis>The task won't be removed when it is empty. Removal has to be an explicit request
@@ -921,10 +929,8 @@
             return;
         }
 
-        if (isLeafTask()) {
-            // This task is going away, so save the last state if necessary.
-            saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
-        }
+        // This task is going away, so save the last state if necessary.
+        saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
 
         // TODO: VI what about activity?
         final boolean isVoiceSession = voiceSession != null;
@@ -2250,6 +2256,21 @@
 
         if (pipChanging) {
             mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
+            // If the top activity is using fixed rotation, it should be changing from PiP to
+            // fullscreen with display orientation change. Do not notify fullscreen task organizer
+            // because the restoration of task surface and the transformation of activity surface
+            // need to be done synchronously.
+            final ActivityRecord r = topRunningActivity();
+            if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
+                mForceNotOrganized = true;
+            }
+        } else if (mForceNotOrganized) {
+            // If the display orientation change is done, let the corresponding task organizer take
+            // back the control of this task.
+            final ActivityRecord r = topRunningActivity();
+            if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) {
+                mForceNotOrganized = false;
+            }
         }
         try {
             // We have 2 reasons why we need to report orientation change here.
@@ -2459,14 +2480,16 @@
 
     /**
      * Saves launching state if necessary so that we can launch the activity to its latest state.
-     * It only saves state if this task has been shown to user and it's in fullscreen or freeform
-     * mode on freeform displays.
      */
     private void saveLaunchingStateIfNeeded() {
         saveLaunchingStateIfNeeded(getDisplayContent());
     }
 
     private void saveLaunchingStateIfNeeded(DisplayContent display) {
+        if (!isLeafTask()) {
+            return;
+        }
+
         if (!getHasBeenVisible()) {
             // Not ever visible to user.
             return;
@@ -2837,6 +2860,9 @@
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
             windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
         }
+        // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+        // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+        getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
         Rect outOverrideBounds =
                 getResolvedOverrideConfiguration().windowConfiguration.getBounds();
 
@@ -2867,16 +2893,6 @@
         // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
         outBounds.setEmpty();
         computeLetterboxBounds(outBounds, newParentConfig);
-        // Since the task is letterboxed due to mismatched orientation against its parent,
-        // sandbox max bounds to the app bounds.
-        if (!outBounds.isEmpty()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
-                                + "orientation with parent, to %s vs DisplayArea %s", outBounds,
-                        getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
-            }
-            getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
-        }
     }
 
     /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
@@ -4545,6 +4561,9 @@
         pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
         pw.print(" isResizeable="); pw.println(isResizeable());
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+        if (mForceNotOrganized) {
+            pw.print(prefix); pw.println("mForceNotOrganized=true");
+        }
         pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
     }
 
@@ -4989,6 +5008,9 @@
     }
 
     private boolean canBeOrganized() {
+        if (mForceNotOrganized) {
+            return false;
+        }
         // All root tasks can be organized
         if (isRootTask()) {
             return true;
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index e18219e..622fe05 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -497,7 +497,7 @@
     void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
         final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
                 taskId, newDisplayId);
-        forAllLocalListeners(mNotifyTaskStackChanged, msg);
+        forAllLocalListeners(mNotifyTaskDisplayChanged, msg);
         msg.sendToTarget();
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..40248c4 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
@@ -403,7 +404,11 @@
         }
         // We don't allow untrusted display to top when root task moves to top,
         // until user tapping this display to change display position as top intentionally.
-        if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
+        //
+        // Displays with {@code mDontMoveToTop} property set to {@code true} won't be
+        // allowed to top neither.
+        if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop)
+                && !getParent().isOnTop()) {
             includingParents = false;
         }
         final int targetPosition = findPositionForRootTask(position, child, false /* adding */);
@@ -905,6 +910,13 @@
         }
     }
 
+    @Override
+    RemoteAnimationTarget createRemoteAnimationTarget(
+            RemoteAnimationController.RemoteAnimationRecord record) {
+        final ActivityRecord activity = getTopMostActivity();
+        return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+    }
+
     SurfaceControl getSplitScreenDividerAnchor() {
         return mSplitScreenDividerAnchor;
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
                     dc.mWallpaperController.startWallpaperAnimation(anim);
                 }
             }
-        }
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             dc.startKeyguardExitOnNonAppWindows(
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index f572e8e..43303d4 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.INVALID_UID;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -51,7 +50,7 @@
 
     WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
             DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
-        super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
+        super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens,
                 false /* roundedCornerOverlay */, false /* fromClientToken */, options);
         dc.mWallpaperController.addWallpaperToken(this);
         setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 52ed278..eb32486 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -26,7 +26,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 
 import android.content.Context;
 import android.os.Trace;
@@ -134,8 +133,10 @@
         // Schedule next frame already such that back-pressure happens continuously.
         scheduleAnimation();
 
+        final RootWindowContainer root = mService.mRoot;
         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
-        mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+        mBulkUpdateParams = 0;
+        root.mOrientationChangeComplete = true;
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
         }
@@ -144,14 +145,14 @@
         mService.openSurfaceTransaction();
         try {
             // Remove all deferred displays, tasks, and activities.
-            mService.mRoot.handleCompleteDeferredRemoval();
+            root.handleCompleteDeferredRemoval();
 
             final AccessibilityController accessibilityController =
                     mService.mAccessibilityController;
             final int numDisplays = mDisplayContentsAnimators.size();
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                final DisplayContent dc = root.getDisplayContent(displayId);
                 // Update animations of all applications, including those associated with
                 // exiting/removed apps.
                 dc.updateWindowsForAnimator();
@@ -160,11 +161,11 @@
 
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                final DisplayContent dc = root.getDisplayContent(displayId);
 
                 dc.checkAppWindowsReadyToShow();
                 if (accessibilityController != null) {
-                    accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+                    accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
                             mTransaction);
                 }
             }
@@ -179,13 +180,14 @@
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         }
 
-        final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
-        final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+        final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
+        final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
+                && root.copyAnimToLayoutParams();
         if (hasPendingLayoutChanges || doRequest) {
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
-        final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+        final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
                 ANIMATION_TYPE_ALL /* typesToCheck */);
         if (rootAnimating && !mLastRootAnimating) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
@@ -197,7 +199,7 @@
         mLastRootAnimating = rootAnimating;
 
         final boolean runningExpensiveAnimations =
-                mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+                root.isAnimating(TRANSITION | CHILDREN /* flags */,
                         ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
                                 | ANIMATION_TYPE_RECENTS /* typesToCheck */);
         if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
@@ -216,12 +218,10 @@
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
 
         if (mRemoveReplacedWindows) {
-            mService.mRoot.removeReplacedWindows();
+            root.removeReplacedWindows();
             mRemoveReplacedWindows = false;
         }
 
-        mService.destroyPreservedSurfaceLocked();
-
         mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
         executeAfterPrepareSurfacesRunnables();
 
@@ -237,8 +237,8 @@
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
             builder.append(" UPDATE_ROTATION");
         }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
-            builder.append(" ORIENTATION_CHANGE_COMPLETE");
+        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
+            builder.append(" SET_WALLPAPER_ACTION_PENDING");
         }
         return builder.toString();
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..e183ea0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,12 +23,15 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.view.Display;
 import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 
@@ -46,6 +49,41 @@
 public abstract class WindowManagerInternal {
 
     /**
+     * Interface for accessibility features implemented by AccessibilityController inside
+     * WindowManager.
+     */
+    public interface AccessibilityControllerInternal {
+        /**
+         * Enable the accessibility trace logging.
+         */
+        void startTrace();
+
+        /**
+         * Disable the accessibility trace logging.
+         */
+        void stopTrace();
+
+        /**
+         * Is trace enabled or not.
+         */
+        boolean isEnabled();
+
+        /**
+         * Add an accessibility trace entry.
+         *
+         * @param where A string to identify this log entry, which can be used to filter/search
+         *        through the tracing file.
+         * @param callingParams The parameters for the method to be logged.
+         * @param a11yDump The proto byte array for a11y state when the entry is generated.
+         * @param callingUid The calling uid.
+         * @param stackTrace The stack trace, null if not needed.
+         */
+        void logTrace(
+                String where, String callingParams, byte[] a11yDump, int callingUid,
+                StackTraceElement[] stackTrace);
+    }
+
+    /**
      * Interface to receive a callback when the windows reported for
      * accessibility changed.
      */
@@ -154,6 +192,21 @@
     }
 
     /**
+     * An interface to be notified when keyguard exit animation should start.
+     */
+    public interface KeyguardExitAnimationStartListener {
+        /**
+         * Called when keyguard exit animation should start.
+         * @param apps The list of apps to animate.
+         * @param wallpapers The list of wallpapers to animate.
+         * @param finishedCallback The callback to invoke when the animation is finished.
+         */
+        void onAnimationStart(RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                IRemoteAnimationFinishedCallback finishedCallback);
+    }
+
+    /**
       * An interface to be notified about hardware keyboard status.
       */
     public interface OnHardKeyboardStatusChangeListener {
@@ -207,6 +260,11 @@
     }
 
     /**
+     * Request the interface to access features implemented by AccessibilityController.
+     */
+    public abstract AccessibilityControllerInternal getAccessibilityController();
+
+    /**
      * Request that the window manager call
      * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
      * within a surface transaction at a later time.
@@ -351,8 +409,10 @@
      * @param token The token to add.
      * @param type The window type.
      * @param displayId The display to add the token to.
+     * @param options A bundle used to pass window-related options.
      */
-    public abstract void addWindowToken(android.os.IBinder token, int type, int displayId);
+    public abstract void addWindowToken(@NonNull android.os.IBinder token, int type, int displayId,
+            @Nullable Bundle options);
 
     /**
      * Removes a window token.
@@ -372,6 +432,14 @@
     public abstract void registerAppTransitionListener(AppTransitionListener listener);
 
     /**
+     * Registers a listener to be notified to start the keyguard exit animation.
+     *
+     * @param listener The listener to register.
+     */
+    public abstract void registerKeyguardExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener);
+
+    /**
      * Reports that the password for the given user has changed.
      */
     public abstract void reportPasswordChanged(int userId);
@@ -543,9 +611,9 @@
     public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
 
     /**
-     * Checks if this display is touchable.
+     * Checks if the device supports touch or faketouch.
      */
-    public abstract boolean isTouchableDisplay(int displayId);
+    public abstract boolean isTouchOrFaketouchDevice();
 
     /**
      * Returns the info associated with the input token used to determine if a key should be
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6319c80..c54c978 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -23,7 +23,6 @@
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
 import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
-import static android.Manifest.permission.STATUS_BAR_SERVICE;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -35,7 +34,6 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -82,8 +80,6 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
-import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -104,6 +100,7 @@
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_FREEZE_DISPLAY;
 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
 import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -405,6 +402,8 @@
     // trying to apply a new one.
     private static final boolean ALWAYS_KEEP_CURRENT = true;
 
+    static final int LOGTAG_INPUT_FOCUS = 62001;
+
     /**
      * Restrict ability of activities overriding transition animation in a way such that
      * an activity can do it only when the transition happens within a same task.
@@ -413,7 +412,6 @@
      */
     private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
             "persist.wm.disable_custom_task_animation";
-    static final int LOGTAG_INPUT_FOCUS = 62001;
 
     /**
      * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +419,19 @@
     static boolean sDisableCustomTaskAnimationProperty =
             SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    public static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
             "ro.sf.disable_triple_buffer";
 
@@ -444,12 +455,6 @@
 
     private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
 
-    /** The maximum count of window tokens without surface that an app can register. */
-    private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5;
-
-    /** System UI can create more window context... */
-    private static final int SYSTEM_UI_MULTIPLIER = 2;
-
     /**
      * Override of task letterbox aspect ratio that is set via ADB with
      * set-task-letterbox-aspect-ratio or via {@link
@@ -626,13 +631,6 @@
     final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
 
     /**
-     * Windows with a preserved surface waiting to be destroyed. These windows
-     * are going through a surface change. We keep the old surface around until
-     * the first frame on the new surface finishes drawing.
-     */
-    final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
-
-    /**
      * This is set when we have run out of memory, and will either be an empty
      * list or contain windows that need to be force removed.
      */
@@ -753,6 +751,7 @@
     final TaskSnapshotController mTaskSnapshotController;
 
     boolean mIsTouchDevice;
+    boolean mIsFakeTouchDevice;
 
     final H mH = new H();
 
@@ -978,7 +977,7 @@
         void updateSupportsNonResizableMultiWindow() {
             ContentResolver resolver = mContext.getContentResolver();
             final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver,
-                    DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+                    DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
 
             mAtmService.mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
         }
@@ -1604,7 +1603,7 @@
                     final Bundle options = mWindowContextListenerController
                             .getOptions(windowContextToken);
                     token = new WindowToken(this, binder, type, false /* persistOnEmpty */,
-                            displayContent, session.mCanAddInternalSystemWindow, callingUid,
+                            displayContent, session.mCanAddInternalSystemWindow,
                             isRoundedCornerOverlay, true /* fromClientToken */, options);
                 } else {
                     final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
@@ -2151,25 +2150,9 @@
         Slog.i(tag, s, e);
     }
 
-    void setTransparentRegionWindow(Session session, IWindow client, Region region) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                WindowState w = windowForClientLocked(session, client, false);
-                ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s",
-                        region, w);
-
-                if ((w != null) && w.mHasSurface) {
-                    w.mWinAnimator.setTransparentRegionHintLocked(region);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
             Rect visibleInsets, Region touchableRegion) {
+        int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -2195,8 +2178,8 @@
 
                     // We need to report touchable region changes to accessibility.
                     if (mAccessibilityController != null) {
-                        mAccessibilityController.onSomeWindowResizedOrMovedLocked(
-                                w.getDisplayContent().getDisplayId());
+                        mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+                                uid, w.getDisplayContent().getDisplayId());
                     }
                 }
             }
@@ -2210,7 +2193,7 @@
             if (mAccessibilityController != null) {
                 WindowState window = mWindowMap.get(token);
                 if (window != null) {
-                    mAccessibilityController.onRectangleOnScreenRequestedLocked(
+                    mAccessibilityController.onRectangleOnScreenRequested(
                             window.getDisplayId(), rectangle);
                 }
             }
@@ -2313,8 +2296,8 @@
                 if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
                         && (mAccessibilityController != null)) {
                     // No move or resize, but the controller checks for title changes as well
-                    mAccessibilityController.onSomeWindowResizedOrMovedLocked(
-                            win.getDisplayContent().getDisplayId());
+                    mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+                            uid, win.getDisplayContent().getDisplayId());
                 }
 
                 if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
@@ -2329,7 +2312,6 @@
 
             if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
                     + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
-            winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
             if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
                 winAnimator.mAlpha = attrs.alpha;
             }
@@ -2615,7 +2597,7 @@
             win.destroySurface(false, stopped);
         }
         if (mAccessibilityController != null) {
-            mAccessibilityController.onWindowTransitionLocked(win, transit);
+            mAccessibilityController.onWindowTransition(win, transit);
         }
 
         return focusMayChange;
@@ -2708,91 +2690,36 @@
     }
 
     @Override
-    public void addWindowToken(IBinder binder, int type, int displayId) {
-        addWindowTokenWithOptions(binder, type, displayId, null /* options */,
-                null /* packageName */, false /* fromClientToken */);
-    }
-
-    @Override
-    public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
-            String packageName) {
-        if (tokenCountExceed()) {
-            return ADD_TOO_MANY_TOKENS;
+    public void addWindowToken(@NonNull IBinder binder, int type, int displayId,
+            @Nullable Bundle options) {
+        if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        return addWindowTokenWithOptions(binder, type, displayId, options, packageName,
-                true /* fromClientToken */);
-    }
 
-    private boolean tokenCountExceed() {
-        final int callingUid = Binder.getCallingUid();
-        // Don't check if caller is from system server.
-        if (callingUid == myPid()) {
-            return false;
-        }
-        final int limit =
-                (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions"))
-                        ?  MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER
-                        : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE;
         synchronized (mGlobalLock) {
-            int[] count = new int[1];
-            mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid));
-            return count[0] >= limit;
-        }
-    }
+            final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
+            if (dc == null) {
+                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+                        + " for non-exiting displayId=%d", binder, displayId);
+                return;
+            }
 
-    private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
-            String packageName, boolean fromClientToken) {
-        final boolean callerCanManageAppTokens =
-                checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
-        // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
-        // by checkAddPermission.
-        if (!callerCanManageAppTokens) {
-            final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
-                    packageName, new int[1]);
-            if (res != ADD_OKAY) {
-                return res;
+            WindowToken token = dc.getWindowToken(binder);
+            if (token != null) {
+                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+                        + " for already created window token: %s"
+                        + " displayId=%d", binder, token, displayId);
+                return;
+            }
+            if (type == TYPE_WALLPAPER) {
+                new WallpaperWindowToken(this, binder, true, dc,
+                        true /* ownerCanManageAppTokens */, options);
+            } else {
+                new WindowToken(this, binder, type, true /* persistOnEmpty */, dc,
+                        true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+                        false /* fromClientToken */, options);
             }
         }
-
-        final int callingUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                if (!callerCanManageAppTokens) {
-                    if (packageName == null || !unprivilegedAppCanCreateTokenWith(
-                            null /* parentWindow */, callingUid, type, type, binder,
-                            packageName)) {
-                        throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-                    }
-                }
-
-                final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
-                if (dc == null) {
-                    ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
-                            + " for non-exiting displayId=%d", binder, displayId);
-                    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
-                }
-
-                WindowToken token = dc.getWindowToken(binder);
-                if (token != null) {
-                    ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
-                            + " for already created window token: %s"
-                            + " displayId=%d", binder, token, displayId);
-                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
-                }
-                // TODO(window-container): Clean up dead tokens
-                if (type == TYPE_WALLPAPER) {
-                    new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
-                            options);
-                } else {
-                    new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
-                            callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-        return WindowManagerGlobal.ADD_OKAY;
     }
 
     /**
@@ -2872,38 +2799,26 @@
 
     @Override
     public void removeWindowToken(IBinder binder, int displayId) {
-        final boolean callerCanManageAppTokens =
-                checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
-        final WindowToken windowToken;
-        synchronized (mGlobalLock) {
-            windowToken = mRoot.getWindowToken(binder);
+        if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        if (windowToken == null) {
-            ProtoLog.w(WM_ERROR,
-                    "removeWindowToken: Attempted to remove non-existing token: %s", binder);
-            return;
-        }
-        final int callingUid = Binder.getCallingUid();
-
-        // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
-        // remove the window tokens which they added themselves.
-        if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
-                || callingUid != windowToken.getOwnerUid())) {
-            throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
-                    + " to remove token owned by another uid");
-        }
-
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
+
                 if (dc == null) {
                     ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
                             + " for non-exiting displayId=%d", binder, displayId);
                     return;
                 }
-
-                dc.removeWindowToken(binder);
+                final WindowToken token = dc.removeWindowToken(binder);
+                if (token == null) {
+                    ProtoLog.w(WM_ERROR,
+                            "removeWindowToken: Attempted to remove non-existing token: %s",
+                            binder);
+                    return;
+                }
                 dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             }
         } finally {
@@ -5030,6 +4945,8 @@
             mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
+            mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_FAKETOUCH);
         }
 
         try {
@@ -5504,14 +5421,6 @@
         }
     }
 
-    void destroyPreservedSurfaceLocked() {
-        for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
-            final WindowState w = mDestroyPreservedSurface.get(i);
-            w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction());
-        }
-        mDestroyPreservedSurface.clear();
-    }
-
     // -------------------------------------------------------------
     // IWindowManager API
     // -------------------------------------------------------------
@@ -5804,8 +5713,6 @@
         if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
             w.setOrientationChanging(true);
-            w.mLastFreezeDuration = 0;
-            mRoot.mOrientationChangeComplete = false;
             if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
                 mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
                 // XXX should probably keep timeout from
@@ -5916,6 +5823,9 @@
                             "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
                             exitAnim, enterAnim, Debug.getCallers(8));
         mScreenFrozenLock.acquire();
+        // Apply launch power mode to reduce screen frozen time because orientation change may
+        // relaunch activity and redraw windows. This may also help speed up user switching.
+        mAtmService.startLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
 
         mDisplayFrozen = true;
         mDisplayFreezeTime = SystemClock.elapsedRealtime();
@@ -6059,6 +5969,7 @@
         if (configChanged) {
             displayContent.sendNewConfiguration();
         }
+        mAtmService.endLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
         mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
 
@@ -7154,6 +7065,7 @@
         checkCallerOwnsDisplay(displayId);
 
         synchronized (mGlobalLock) {
+            int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 final WindowState win = windowForClientLocked(null, client, false);
@@ -7165,8 +7077,8 @@
                 // Notifies AccessibilityController to re-compute the window observer of
                 // this embedded display
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId,
-                            win);
+                    mAccessibilityController.handleWindowObserverOfEmbeddedDisplay(
+                            displayId, win, uid);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -7532,6 +7444,12 @@
     private final class LocalService extends WindowManagerInternal {
 
         @Override
+        public AccessibilityControllerInternal getAccessibilityController() {
+            return AccessibilityController.getAccessibilityControllerInternal(
+                    WindowManagerService.this);
+        }
+
+        @Override
         public void clearSnapshotCache() {
             synchronized (mGlobalLock) {
                 mTaskSnapshotController.clearSnapshotCache();
@@ -7549,7 +7467,7 @@
         public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.setMagnificationSpecLocked(displayId, spec);
+                    mAccessibilityController.setMagnificationSpec(displayId, spec);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -7560,7 +7478,7 @@
         public void setForceShowMagnifiableBounds(int displayId, boolean show) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show);
+                    mAccessibilityController.setForceShowMagnifiableBounds(displayId, show);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -7571,8 +7489,7 @@
         public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
             synchronized (mGlobalLock) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.getMagnificationRegionLocked(displayId,
-                            magnificationRegion);
+                    mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
@@ -7588,7 +7505,7 @@
                 }
                 MagnificationSpec spec = null;
                 if (mAccessibilityController != null) {
-                    spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+                    spec = mAccessibilityController.getMagnificationSpecForWindow(windowState);
                 }
                 if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
                     return null;
@@ -7610,9 +7527,9 @@
                     mAccessibilityController = new AccessibilityController(
                             WindowManagerService.this);
                 }
-                boolean result = mAccessibilityController.setMagnificationCallbacksLocked(
+                boolean result = mAccessibilityController.setMagnificationCallbacks(
                         displayId, callbacks);
-                if (!mAccessibilityController.hasCallbacksLocked()) {
+                if (!mAccessibilityController.hasCallbacks()) {
                     mAccessibilityController = null;
                 }
                 return result;
@@ -7628,9 +7545,9 @@
                             WindowManagerService.this);
                 }
                 final boolean result =
-                        mAccessibilityController.setWindowsForAccessibilityCallbackLocked(
+                        mAccessibilityController.setWindowsForAccessibilityCallback(
                         displayId, callback);
-                if (!mAccessibilityController.hasCallbacksLocked()) {
+                if (!mAccessibilityController.hasCallbacks()) {
                     mAccessibilityController = null;
                 }
                 return result;
@@ -7719,8 +7636,9 @@
         }
 
         @Override
-        public void addWindowToken(IBinder token, int type, int displayId) {
-            WindowManagerService.this.addWindowToken(token, type, displayId);
+        public void addWindowToken(IBinder token, int type, int displayId,
+                @Nullable Bundle options) {
+            WindowManagerService.this.addWindowToken(token, type, displayId, options);
         }
 
         @Override
@@ -7763,6 +7681,15 @@
         }
 
         @Override
+        public void registerKeyguardExitAnimationStartListener(
+                KeyguardExitAnimationStartListener listener) {
+            synchronized (mGlobalLock) {
+                getDefaultDisplayContentLocked().mAppTransition
+                        .registerKeygaurdExitAnimationStartListener(listener);
+            }
+        }
+
+        @Override
         public void reportPasswordChanged(int userId) {
             mKeyguardDisableHandler.updateKeyguardEnabled(userId);
         }
@@ -7819,7 +7746,7 @@
                 accessibilityController = mAccessibilityController;
             }
             if (accessibilityController != null) {
-                accessibilityController.performComputeChangedWindowsNotLocked(displayId, true);
+                accessibilityController.performComputeChangedWindowsNot(displayId, true);
             }
         }
 
@@ -8018,13 +7945,10 @@
         }
 
         @Override
-        public boolean isTouchableDisplay(int displayId) {
+        public boolean isTouchOrFaketouchDevice() {
             synchronized (mGlobalLock) {
-                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                final Configuration configuration =
-                        displayContent != null ? displayContent.getConfiguration() : null;
-                return configuration != null
-                        && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
+                // All touchable devices are also faketouchable.
+                return mIsFakeTouchDevice;
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index da31bb2..5ef9420 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -16,25 +16,36 @@
 
 package com.android.server.wm;
 
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+
 import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
 import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
 
+import android.app.ActivityThread;
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.rotationresolver.RotationResolverInternal;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A special helper class used by the WindowManager
@@ -55,6 +66,9 @@
 
     private static final boolean USE_GRAVITY_SENSOR = false;
     private static final int DEFAULT_BATCH_LATENCY = 100000;
+    private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled
+    private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis";
+    private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
 
     private Handler mHandler;
     private SensorManager mSensorManager;
@@ -62,7 +76,13 @@
     private int mRate;
     private String mSensorType;
     private Sensor mSensor;
-    private OrientationJudge mOrientationJudge;
+
+    @VisibleForTesting
+    OrientationJudge mOrientationJudge;
+
+    @VisibleForTesting
+    RotationResolverInternal mRotationResolverService;
+
     private int mCurrentRotation = -1;
     private final Context mContext;
     private final WindowManagerConstants mConstants;
@@ -256,6 +276,32 @@
     }
 
     /**
+     * Returns true if the current status of the phone is suitable for using rotation resolver
+     * service.
+     *
+     * To reduce the power consumption of rotation resolver service, rotation query should run less
+     * frequently than other low power orientation sensors. This method is used to check whether
+     * the current status of the phone is necessary to request a suggested screen rotation from the
+     * rotation resolver service. Note that it always returns {@code false} in the base class. It
+     * should be overridden in the derived classes.
+     */
+    public boolean canUseRotationResolver() {
+        return false;
+    }
+
+    /**
+     * Returns true if the rotation resolver feature is enabled by setting. It means {@link
+     * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate
+     * screen rotation.
+     */
+    @VisibleForTesting
+    boolean isRotationResolverEnabled() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED,
+                UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
      * Called when the rotation view of the device has changed.
      *
      * This method is called whenever the orientation becomes certain of an orientation.
@@ -1045,6 +1091,30 @@
         private int mProposedRotation = -1;
         private int mDesiredRotation = -1;
         private boolean mRotationEvaluationScheduled;
+        private long mRotationResolverTimeoutMillis;
+
+        OrientationSensorJudge() {
+            super();
+            setupRotationResolverParameters();
+        }
+
+        private void setupRotationResolverParameters() {
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER,
+                    ActivityThread.currentApplication().getMainExecutor(), (properties) -> {
+                        final Set<String> keys = properties.getKeyset();
+                        if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) {
+                            readRotationResolverParameters();
+                        }
+                    });
+            readRotationResolverParameters();
+        }
+
+        private void readRotationResolverParameters() {
+            mRotationResolverTimeoutMillis = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                    KEY_ROTATION_RESOLVER_TIMEOUT,
+                    DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS);
+        }
 
         @Override
         public int getProposedRotationLocked() {
@@ -1069,19 +1139,13 @@
 
         @Override
         public void onSensorChanged(SensorEvent event) {
-            int newRotation;
-
             int reportedRotation = (int) event.values[0];
             if (reportedRotation < 0 || reportedRotation > 3) {
                 return;
             }
 
-            synchronized (mLock) {
-                mDesiredRotation = reportedRotation;
-                newRotation = evaluateRotationChangeLocked();
-            }
-            if (newRotation >= 0) {
-                onProposedRotationChanged(newRotation);
+            // Log raw sensor rotation.
+            if (evaluateRotationChangeLocked() >= 0) {
                 if (mConstants.mRawSensorLoggingEnabled) {
                     FrameworkStatsLog.write(
                             FrameworkStatsLog.DEVICE_ROTATED,
@@ -1089,6 +1153,35 @@
                             rotationToLogEnum(reportedRotation));
                 }
             }
+
+            if (isRotationResolverEnabled() && canUseRotationResolver()) {
+                if (mRotationResolverService == null) {
+                    mRotationResolverService = LocalServices.getService(
+                            RotationResolverInternal.class);
+                }
+
+                final CancellationSignal cancellationSignal = new CancellationSignal();
+                mRotationResolverService.resolveRotation(
+                        new RotationResolverInternal.RotationResolverCallbackInternal() {
+                            @Override
+                            public void onSuccess(int result) {
+                                finalizeRotation(result);
+                            }
+
+                            @Override
+                            public void onFailure(int error) {
+                                finalizeRotation(reportedRotation);
+                            }
+                        },
+                        reportedRotation,
+                        mCurrentRotation,
+                        mRotationResolverTimeoutMillis,
+                        cancellationSignal);
+                getHandler().postDelayed(cancellationSignal::cancel,
+                        mRotationResolverTimeoutMillis);
+            } else {
+                finalizeRotation(reportedRotation);
+            }
         }
 
         @Override
@@ -1131,6 +1224,17 @@
             return -1;
         }
 
+        private void finalizeRotation(int reportedRotation) {
+            int newRotation;
+            synchronized (mLock) {
+                mDesiredRotation = reportedRotation;
+                newRotation = evaluateRotationChangeLocked();
+            }
+            if (newRotation >= 0) {
+                onProposedRotationChanged(newRotation);
+            }
+        }
+
         private boolean isDesiredRotationAcceptableLocked(long now) {
             if (mTouching) {
                 return false;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8b4d415..264a3b4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1329,6 +1329,10 @@
             mHasPendingConfigurationChange = true;
             return;
         }
+        dispatchConfiguration(config);
+    }
+
+    void dispatchConfiguration(Configuration config) {
         mHasPendingConfigurationChange = false;
         if (mThread == null) {
             if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1367,8 +1371,13 @@
         mPauseConfigurationDispatchCount++;
     }
 
-    void resumeConfigurationDispatch() {
+    /** Returns {@code true} if the configuration change is pending to dispatch. */
+    boolean resumeConfigurationDispatch() {
+        if (mPauseConfigurationDispatchCount == 0) {
+            return false;
+        }
         mPauseConfigurationDispatchCount--;
+        return mHasPendingConfigurationChange;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 661118f..a94b0aa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -104,7 +104,6 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -1491,6 +1490,10 @@
     void setOrientationChanging(boolean changing) {
         mOrientationChanging = changing;
         mOrientationChangeTimedOut = false;
+        if (changing) {
+            mLastFreezeDuration = 0;
+            mWmService.mRoot.mOrientationChangeComplete = false;
+        }
     }
 
     void orientationChangeTimedOut() {
@@ -1722,7 +1725,7 @@
 
     @Override
     boolean isVisibleRequested() {
-        return isVisible();
+        return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
     }
 
     /**
@@ -2021,7 +2024,7 @@
                 final int winTransit = TRANSIT_EXIT;
                 mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
                 if (accessibilityController != null) {
-                    accessibilityController.onWindowTransitionLocked(this, winTransit);
+                    accessibilityController.onWindowTransition(this, winTransit);
                 }
             }
             setDisplayLayoutNeeded();
@@ -2035,7 +2038,7 @@
         if (isVisibleNow()) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
             if (mWmService.mAccessibilityController != null) {
-                mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+                mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
             }
             changed = true;
             if (displayContent != null) {
@@ -2113,7 +2116,7 @@
         }
 
         if (mWmService.mAccessibilityController != null) {
-            mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
         }
         updateLocationInParentDisplayIfNeeded();
 
@@ -2133,7 +2136,8 @@
                 && !mAnimatingExit
                 && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
                     || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
-                && (!mIsChildWindow || !getParentWindow().hasMoved());
+                && (!mIsChildWindow || !getParentWindow().hasMoved())
+                && !mWmService.mAtmService.getTransitionController().isCollecting();
     }
 
     boolean isObscuringDisplay() {
@@ -2243,7 +2247,6 @@
 
         disposeInputChannel();
 
-        mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
         mWinAnimator.destroySurfaceLocked(mTmpTransaction);
         mTmpTransaction.apply();
         mSession.windowRemovedLocked();
@@ -2347,7 +2350,7 @@
                         mWmService.requestTraversal();
                     }
                     if (mWmService.mAccessibilityController != null) {
-                        mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+                        mWmService.mAccessibilityController.onWindowTransition(this, transit);
                     }
                 }
                 final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS,
@@ -3097,7 +3100,7 @@
     }
 
     void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
-        if (!mSession.mOverlaysCanBeHidden
+        if (mSession.mCanAddInternalSystemWindow
                 || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
             return;
         }
@@ -3288,7 +3291,6 @@
             ProtoLog.v(WM_DEBUG_ORIENTATION,
                     "set mOrientationChanging of %s", this);
             setOrientationChanging(true);
-            mWmService.mRoot.mOrientationChangeComplete = false;
         }
         mLastFreezeDuration = 0;
         setDisplayLayoutNeeded();
@@ -3309,11 +3311,6 @@
             return destroyedSomething;
         }
 
-        if (appStopped || mWindowRemovalAllowed) {
-            mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction);
-            mTmpTransaction.apply();
-        }
-
         if (mDestroying) {
             ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
                     + " destroySurfaces: appStopped=%b"
@@ -3642,6 +3639,11 @@
         if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
             return;
         }
+        // If the activity is invisible or going invisible, don't report either since it is going
+        // away. This is likely during a transition so we want to preserve the original state.
+        if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+            return;
+        }
 
         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
@@ -3679,7 +3681,7 @@
                     displayId);
 
             if (mWmService.mAccessibilityController != null) {
-                mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
+                mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
             }
             updateLocationInParentDisplayIfNeeded();
         } catch (RemoteException e) {
@@ -4782,7 +4784,7 @@
             return;
         }
         if (mWmService.mAccessibilityController != null) {
-            mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
         }
 
         if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -5001,23 +5003,8 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        // When we change the Surface size, in scenarios which may require changing
-        // the surface position in sync with the resize, we use a preserved surface
-        // so we can freeze it while waiting for the client to report draw on the newly
-        // sized surface. At the moment this logic is only in place for switching
-        // in and out of the big surface for split screen resize.
         if (isDragResizeChanged()) {
             setDragResizing();
-            // We can only change top level windows to the full-screen surface when
-            // resizing (as we only have one full-screen surface). So there is no need
-            // to preserve and destroy windows which are attached to another, they
-            // will keep their surface and its size may change over time.
-            if (mHasSurface && !isChildWindow()) {
-                mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
-                result |= RELAYOUT_RES_SURFACE_CHANGED |
-                    RELAYOUT_RES_FIRST_TIME;
-                scheduleAnimation();
-            }
         }
         final boolean freeformResizing = isDragResizing()
                 && getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
@@ -5326,7 +5313,7 @@
         updateSurfacePositionNonOrganized();
         // Send information to SufaceFlinger about the priority of the current window.
         updateFrameRateSelectionPriorityIfNeeded();
-        updateGlobalScaleIfNeeded();
+        if (isVisibleRequested()) updateGlobalScaleIfNeeded();
 
         mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
         super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c8b940a..ece256e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -52,7 +52,6 @@
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -116,15 +115,7 @@
     boolean mAnimationIsEntrance;
 
     WindowSurfaceController mSurfaceController;
-    private WindowSurfaceController mPendingDestroySurface;
 
-    /**
-     * Set if the client has asked that the destroy of its surface be delayed
-     * until it explicitly says it is okay.
-     */
-    boolean mSurfaceDestroyDeferred;
-
-    private boolean mDestroyPreservedSurfaceUponRedraw;
     float mShownAlpha = 0;
     float mAlpha = 0;
     float mLastAlpha = 0;
@@ -257,11 +248,6 @@
             //dump();
             mLastHidden = true;
 
-            // We may have a preserved surface which we no longer need. If there was a quick
-            // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark
-            // it to be destroyed in prepareSurfaceLocked.
-            markPreservedSurfaceForDestroy();
-
             if (mSurfaceController != null) {
                 mSurfaceController.hide(transaction, reason);
             }
@@ -323,70 +309,6 @@
         return result;
     }
 
-    void preserveSurfaceLocked(SurfaceControl.Transaction t) {
-        if (mDestroyPreservedSurfaceUponRedraw) {
-            // This could happen when switching the surface mode very fast. For example,
-            // we preserved a surface when dragResizing changed to true. Then before the
-            // preserved surface is removed, dragResizing changed to false again.
-            // In this case, we need to leave the preserved surface alone, and destroy
-            // the actual surface, so that the createSurface call could create a surface
-            // of the proper size. The preserved surface will still be removed when client
-            // finishes drawing to the new surface.
-            mSurfaceDestroyDeferred = false;
-
-            // Make sure to reparent any children of the new surface back to the preserved
-            // surface before destroying it.
-            if (mSurfaceController != null && mPendingDestroySurface != null) {
-                mPostDrawTransaction.reparentChildren(
-                    mSurfaceController.mSurfaceControl,
-                    mPendingDestroySurface.mSurfaceControl).apply();
-            }
-            destroySurfaceLocked(t);
-            mSurfaceDestroyDeferred = true;
-            return;
-        }
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
-        if (mSurfaceController != null) {
-            // Our SurfaceControl is always at layer 0 within the parent Surface managed by
-            // window-state. We want this old Surface to stay on top of the new one
-            // until we do the swap, so we place it at a positive layer.
-            t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
-        }
-        mDestroyPreservedSurfaceUponRedraw = true;
-        mSurfaceDestroyDeferred = true;
-        destroySurfaceLocked(t);
-    }
-
-    void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) {
-        if (!mDestroyPreservedSurfaceUponRedraw) {
-            return;
-        }
-
-        // If we are preserving a surface but we aren't relaunching that means
-        // we are just doing an in-place switch. In that case any SurfaceFlinger side
-        // child layers need to be reparented to the new surface to make this
-        // transparent to the app.
-        // If the children are detached, we don't want to reparent them to the new surface.
-        // Instead let the children get removed when the old surface is deleted.
-        if (mSurfaceController != null && mPendingDestroySurface != null
-                && !mPendingDestroySurface.mChildrenDetached
-                && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
-            mPostDrawTransaction.reparentChildren(
-                    mPendingDestroySurface.mSurfaceControl,
-                    mSurfaceController.mSurfaceControl).apply();
-        }
-
-        destroyDeferredSurfaceLocked(t);
-        mDestroyPreservedSurfaceUponRedraw = false;
-    }
-
-    private void markPreservedSurfaceForDestroy() {
-        if (mDestroyPreservedSurfaceUponRedraw
-                && !mService.mDestroyPreservedSurface.contains(mWin)) {
-            mService.mDestroyPreservedSurface.add(mWin);
-        }
-    }
-
     void resetDrawState() {
         mDrawState = DRAW_PENDING;
 
@@ -508,39 +430,23 @@
             return;
         }
 
-        // When destroying a surface we want to make sure child windows are hidden. If we are
-        // preserving the surface until redraw though we intend to swap it out with another surface
-        // for resizing. In this case the window always remains visible to the user and the child
-        // windows should likewise remain visible.
-        if (!mDestroyPreservedSurfaceUponRedraw) {
-            mWin.mHidden = true;
-        }
+        mWin.mHidden = true;
 
         try {
-            if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
-                    + mSurfaceController + ", session " + mSession);
-            if (mSurfaceDestroyDeferred) {
-                if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
-                    if (mPendingDestroySurface != null) {
-                        ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
-                                mWin, new RuntimeException().fillInStackTrace());
-                        mPendingDestroySurface.destroy(t);
-                    }
-                    mPendingDestroySurface = mSurfaceController;
-                }
-            } else {
-                ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
-                        mWin, new RuntimeException().fillInStackTrace());
-                destroySurface(t);
+            if (DEBUG_VISIBILITY) {
+                logWithStack(TAG, "Window " + this + " destroying surface "
+                        + mSurfaceController + ", session " + mSession);
             }
+            ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+                    mWin, new RuntimeException().fillInStackTrace());
+            destroySurface(t);
             // Don't hide wallpaper if we're deferring the surface destroy
             // because of a surface change.
-            if (!mDestroyPreservedSurfaceUponRedraw) {
-                mWallpaperControllerLocked.hideWallpapers(mWin);
-            }
+            mWallpaperControllerLocked.hideWallpapers(mWin);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Exception thrown when destroying Window " + this
-                + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+                    + " surface " + mSurfaceController + " session " + mSession + ": "
+                    + e.toString());
         }
 
         // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
@@ -554,27 +460,6 @@
         mDrawState = NO_SURFACE;
     }
 
-    void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) {
-        try {
-            if (mPendingDestroySurface != null) {
-                ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
-                        mWin, new RuntimeException().fillInStackTrace());
-                mPendingDestroySurface.destroy(t);
-                // Don't hide wallpaper if we're destroying a deferred surface
-                // after a surface mode change.
-                if (!mDestroyPreservedSurfaceUponRedraw) {
-                    mWallpaperControllerLocked.hideWallpapers(mWin);
-                }
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception thrown when destroying Window "
-                    + this + " surface " + mPendingDestroySurface
-                    + " session " + mSession + ": " + e.toString());
-        }
-        mSurfaceDestroyDeferred = false;
-        mPendingDestroySurface = null;
-    }
-
     void computeShownFrameLocked() {
         if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
             return;
@@ -744,7 +629,6 @@
             if (prepared && mDrawState == HAS_DRAWN) {
                 if (mLastHidden) {
                     if (showSurfaceRobustlyLocked(t)) {
-                        markPreservedSurfaceForDestroy();
                         mAnimator.requestRemovalOfReplacedWindows(w);
                         mLastHidden = false;
                         if (mIsWallpaper) {
@@ -779,7 +663,7 @@
 
         if (w.getOrientationChanging()) {
             if (!w.isDrawn()) {
-                mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+                w.mWmService.mRoot.mOrientationChangeComplete = false;
                 mAnimator.mLastWindowFreezeSource = w;
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Orientation continue waiting for draw in %s", w);
@@ -794,14 +678,6 @@
         }
     }
 
-    void setTransparentRegionHintLocked(final Region region) {
-        if (mSurfaceController == null) {
-            Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
-            return;
-        }
-        mSurfaceController.setTransparentRegionHint(region);
-    }
-
     boolean setWallpaperOffset(int dx, int dy, float scale) {
         if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
             return false;
@@ -905,20 +781,6 @@
         if (!shown)
             return false;
 
-        // If we had a preserved surface it's no longer needed, and it may be harmful
-        // if we are transparent.
-        if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
-            final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
-            mPostDrawTransaction.reparent(pendingSurfaceControl, null);
-            // If the children are detached, we don't want to reparent them to the new surface.
-            // Instead let the children get removed when the old surface is deleted.
-            if (!mPendingDestroySurface.mChildrenDetached) {
-                mPostDrawTransaction.reparentChildren(
-                        mPendingDestroySurface.mSurfaceControl,
-                        mSurfaceController.mSurfaceControl);
-            }
-        }
-
         t.merge(mPostDrawTransaction);
         return true;
     }
@@ -946,7 +808,7 @@
         }
 
         if (mService.mAccessibilityController != null) {
-            mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
+            mService.mAccessibilityController.onWindowTransition(mWin, transit);
         }
     }
 
@@ -1058,13 +920,6 @@
             pw.println();
         }
 
-        if (mPendingDestroySurface != null) {
-            pw.print(prefix); pw.print("mPendingDestroySurface=");
-                    pw.println(mPendingDestroySurface);
-        }
-        if (mSurfaceDestroyDeferred) {
-                    pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
-        }
         if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
             pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
                     pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f4477d..636f0bb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -55,8 +55,6 @@
     private boolean mSurfaceShown = false;
     private float mSurfaceX = 0;
     private float mSurfaceY = 0;
-    private int mSurfaceW = 0;
-    private int mSurfaceH = 0;
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -82,9 +80,6 @@
             int flags, WindowStateAnimator animator, int windowType) {
         mAnimator = animator;
 
-        mSurfaceW = w;
-        mSurfaceH = h;
-
         title = name;
 
         mService = animator.mService;
@@ -104,8 +99,8 @@
                 .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
                 .setCallsite("WindowSurfaceController");
 
-        final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
-                WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+        final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
+                & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
 
         if (useBLAST) {
             b.setBLASTLayer();
@@ -119,7 +114,6 @@
     void hide(SurfaceControl.Transaction transaction, String reason) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
 
-        mAnimator.destroyPreservedSurfaceLocked(transaction);
         if (mSurfaceShown) {
             hideSurface(transaction);
         }
@@ -200,22 +194,6 @@
         return true;
     }
 
-    void setTransparentRegionHint(final Region region) {
-        if (mSurfaceControl == null) {
-            Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
-            return;
-        }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
-        mService.openSurfaceTransaction();
-        try {
-            getGlobalTransaction().setTransparentRegionHint(mSurfaceControl, region);
-        } finally {
-            mService.closeSurfaceTransaction("setTransparentRegion");
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                    "<<< CLOSE TRANSACTION setTransparentRegion");
-        }
-    }
-
     void setOpaque(boolean isOpaque) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
 
@@ -335,9 +313,7 @@
         pw.print(" layer="); pw.print(mSurfaceLayer);
         pw.print(" alpha="); pw.print(mSurfaceAlpha);
         pw.print(" rect=("); pw.print(mSurfaceX);
-        pw.print(","); pw.print(mSurfaceY);
-        pw.print(") "); pw.print(mSurfaceW);
-        pw.print(" x "); pw.print(mSurfaceH);
+        pw.print(","); pw.print(mSurfaceY); pw.print(") ");
         pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
         pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
         pw.print(", "); pw.print(mLastDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6b9fbcb..2ee5fb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -43,8 +43,7 @@
     private int mLayoutRepeatCount;
 
     static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
-    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
+    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 1;
 
     private boolean mTraversalScheduled;
     private int mDeferDepth = 0;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index cd18311..1c3fe02 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.os.Process.INVALID_UID;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -113,8 +112,6 @@
      */
     private final boolean mFromClientToken;
 
-    private final int mOwnerUid;
-
     /**
      * Used to fix the transform of the token to be rotated to a rotation different than it's
      * display. The window frames and surfaces corresponding to this token will be layouted and
@@ -205,27 +202,19 @@
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
             DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
-        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
-                roundedCornerOverlay, false /* fromClientToken */);
+        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+                roundedCornerOverlay, false /* fromClientToken */, null /* options */);
     }
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
-            DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
-            boolean roundedCornerOverlay, boolean fromClientToken) {
-        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid,
-                roundedCornerOverlay, fromClientToken, null /* options */);
-    }
-
-    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
-            DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
-            boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
+            DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
+            boolean fromClientToken, @Nullable Bundle options) {
         super(service);
         token = _token;
         windowType = type;
         mOptions = options;
         mPersistOnEmpty = persistOnEmpty;
         mOwnerCanManageAppTokens = ownerCanManageAppTokens;
-        mOwnerUid = ownerUid;
         mRoundedCornerOverlay = roundedCornerOverlay;
         mFromClientToken = fromClientToken;
         if (dc != null) {
@@ -641,9 +630,14 @@
     void updateSurfacePosition(SurfaceControl.Transaction t) {
         super.updateSurfacePosition(t);
         if (isFixedRotationTransforming()) {
-            // The window is layouted in a simulated rotated display but the real display hasn't
-            // rotated, so here transforms its surface to fit in the real display.
-            mFixedRotationTransformState.transform(this);
+            final ActivityRecord r = asActivityRecord();
+            final Task rootTask = r != null ? r.getRootTask() : null;
+            // Don't transform the activity in PiP because the PiP task organizer will handle it.
+            if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
+                // The window is laid out in a simulated rotated display but the real display hasn't
+                // rotated, so here transforms its surface to fit in the real display.
+                mFixedRotationTransformState.transform(this);
+            }
         }
     }
 
@@ -739,10 +733,6 @@
                 mRoundedCornerOverlay);
     }
 
-    int getOwnerUid() {
-        return mOwnerUid;
-    }
-
     boolean isFromClient() {
         return mFromClientToken;
     }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index cc7e00a..1c4b034 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -56,10 +56,10 @@
         "com_android_server_vibrator_VibratorController.cpp",
         "com_android_server_VibratorManagerService.cpp",
         "com_android_server_PersistentDataBlockService.cpp",
-        "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "onload.cpp",
+        ":lib_cachedAppOptimizer_native",
         ":lib_networkStatsFactory_native",
     ],
 
@@ -134,7 +134,7 @@
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
         "android.hardware.contexthub@1.0",
-        "android.hardware.gnss-cpp",
+        "android.hardware.gnss-V1-cpp",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
@@ -147,12 +147,12 @@
         "android.hardware.light@2.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-cpp",
+        "android.hardware.power-V1-cpp",
         "android.hardware.power.stats@1.0",
         "android.hardware.power.stats-ndk_platform",
         "android.hardware.thermal@1.0",
         "android.hardware.tv.input@1.0",
-        "android.hardware.vibrator-unstable-cpp",
+        "android.hardware.vibrator-V2-cpp",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
@@ -162,7 +162,7 @@
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
         "android.frameworks.stats@1.0",
-        "android.system.suspend.control-cpp",
+        "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend@1.0",
         "service.incremental",
@@ -193,3 +193,10 @@
         "com_android_server_net_NetworkStatsFactory.cpp",
     ],
 }
+
+filegroup {
+    name: "lib_cachedAppOptimizer_native",
+    srcs: [
+        "com_android_server_am_CachedAppOptimizer.cpp",
+    ],
+}
diff --git a/services/core/jni/com_android_server_VibratorManagerService.cpp b/services/core/jni/com_android_server_VibratorManagerService.cpp
index 71de9bd..5dbb71a 100644
--- a/services/core/jni/com_android_server_VibratorManagerService.cpp
+++ b/services/core/jni/com_android_server_VibratorManagerService.cpp
@@ -24,27 +24,47 @@
 #include <utils/Log.h>
 #include <utils/misc.h>
 
-#include <vibratorservice/VibratorManagerHalWrapper.h>
+#include <vibratorservice/VibratorManagerHalController.h>
 
 #include "com_android_server_VibratorManagerService.h"
 
 namespace android {
 
+static JavaVM* sJvm = nullptr;
+static jmethodID sMethodIdOnComplete;
 static std::mutex gManagerMutex;
-static vibrator::ManagerHalWrapper* gManager GUARDED_BY(gManagerMutex) = nullptr;
+static vibrator::ManagerHalController* gManager GUARDED_BY(gManagerMutex) = nullptr;
 
 class NativeVibratorManagerService {
 public:
-    NativeVibratorManagerService() : mHal(std::make_unique<vibrator::LegacyManagerHalWrapper>()) {}
-    ~NativeVibratorManagerService() = default;
+    NativeVibratorManagerService(JNIEnv* env, jobject callbackListener)
+          : mHal(std::make_unique<vibrator::ManagerHalController>()),
+            mCallbackListener(env->NewGlobalRef(callbackListener)) {
+        LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator manager hal");
+        LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
+                            "Unable to create global reference to vibration callback handler");
+    }
 
-    vibrator::ManagerHalWrapper* hal() const { return mHal.get(); }
+    ~NativeVibratorManagerService() {
+        auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+        jniEnv->DeleteGlobalRef(mCallbackListener);
+    }
+
+    vibrator::ManagerHalController* hal() const { return mHal.get(); }
+
+    std::function<void()> createCallback(jlong vibrationId) {
+        return [vibrationId, this]() {
+            auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+            jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId);
+        };
+    }
 
 private:
-    const std::unique_ptr<vibrator::ManagerHalWrapper> mHal;
+    const std::unique_ptr<vibrator::ManagerHalController> mHal;
+    const jobject mCallbackListener;
 };
 
-vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager() {
+vibrator::ManagerHalController* android_server_VibratorManagerService_getManager() {
     std::lock_guard<std::mutex> lock(gManagerMutex);
     return gManager;
 }
@@ -58,9 +78,9 @@
     }
 }
 
-static jlong nativeInit(JNIEnv* /* env */, jclass /* clazz */) {
+static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) {
     std::unique_ptr<NativeVibratorManagerService> service =
-            std::make_unique<NativeVibratorManagerService>();
+            std::make_unique<NativeVibratorManagerService>(env, callbackListener);
     {
         std::lock_guard<std::mutex> lock(gManagerMutex);
         gManager = service->hal();
@@ -72,6 +92,17 @@
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
 }
 
+static jlong nativeGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorManagerService* service =
+            reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("nativeGetCapabilities failed because native service was not initialized");
+        return 0;
+    }
+    auto result = service->hal()->getCapabilities();
+    return result.isOk() ? static_cast<jlong>(result.value()) : 0;
+}
+
 static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
     NativeVibratorManagerService* service =
             reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
@@ -89,13 +120,61 @@
     return ids;
 }
 
+static jboolean nativePrepareSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+                                    jintArray vibratorIds) {
+    NativeVibratorManagerService* service =
+            reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("nativePrepareSynced failed because native service was not initialized");
+        return JNI_FALSE;
+    }
+    jsize size = env->GetArrayLength(vibratorIds);
+    std::vector<int32_t> ids(size);
+    env->GetIntArrayRegion(vibratorIds, 0, size, reinterpret_cast<jint*>(ids.data()));
+    return service->hal()->prepareSynced(ids).isOk() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean nativeTriggerSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+                                    jlong vibrationId) {
+    NativeVibratorManagerService* service =
+            reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("nativeTriggerSynced failed because native service was not initialized");
+        return JNI_FALSE;
+    }
+    auto callback = service->createCallback(vibrationId);
+    return service->hal()->triggerSynced(callback).isOk() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void nativeCancelSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorManagerService* service =
+            reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("nativeCancelSynced failed because native service was not initialized");
+        return;
+    }
+    service->hal()->cancelSynced();
+}
+
 static const JNINativeMethod method_table[] = {
-        {"nativeInit", "()J", (void*)nativeInit},
+        {"nativeInit",
+         "(Lcom/android/server/VibratorManagerService$OnSyncedVibrationCompleteListener;)J",
+         (void*)nativeInit},
         {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
+        {"nativeGetCapabilities", "(J)J", (void*)nativeGetCapabilities},
         {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds},
+        {"nativePrepareSynced", "(J[I)Z", (void*)nativePrepareSynced},
+        {"nativeTriggerSynced", "(JJ)Z", (void*)nativeTriggerSynced},
+        {"nativeCancelSynced", "(J)V", (void*)nativeCancelSynced},
 };
 
-int register_android_server_VibratorManagerService(JNIEnv* env) {
+int register_android_server_VibratorManagerService(JavaVM* jvm, JNIEnv* env) {
+    sJvm = jvm;
+    auto listenerClassName =
+            "com/android/server/VibratorManagerService$OnSyncedVibrationCompleteListener";
+    jclass listenerClass = FindClassOrDie(env, listenerClassName);
+    sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V");
+
     return jniRegisterNativeMethods(env, "com/android/server/VibratorManagerService", method_table,
                                     NELEM(method_table));
 }
diff --git a/services/core/jni/com_android_server_VibratorManagerService.h b/services/core/jni/com_android_server_VibratorManagerService.h
index 3f2a322..22950c5 100644
--- a/services/core/jni/com_android_server_VibratorManagerService.h
+++ b/services/core/jni/com_android_server_VibratorManagerService.h
@@ -17,11 +17,11 @@
 #ifndef _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H
 #define _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H
 
-#include <vibratorservice/VibratorManagerHalWrapper.h>
+#include <vibratorservice/VibratorManagerHalController.h>
 
 namespace android {
 
-extern vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager();
+extern vibrator::ManagerHalController* android_server_VibratorManagerService_getManager();
 
 } // namespace android
 
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 31cc295..4551d49 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -278,6 +278,11 @@
     return retVal;
 }
 
+static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env,
+                                                                            jobject clazz) {
+    return env->NewStringUTF(CGROUP_FREEZE_PATH);
+}
+
 static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
@@ -286,7 +291,9 @@
          (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
         {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
         {"getBinderFreezeInfo", "(I)I",
-         (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}};
+         (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
+        {"getFreezerCheckPath", "()Ljava/lang/String;",
+         (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}};
 
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
 {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5b587e9..643503d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -106,6 +106,7 @@
     jmethodID notifyFocusChanged;
     jmethodID notifySensorEvent;
     jmethodID notifySensorAccuracy;
+    jmethodID notifyVibratorState;
     jmethodID notifyUntrustedTouch;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
@@ -305,6 +306,7 @@
                            const std::vector<float>& values) override;
     void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
                               InputDeviceSensorAccuracy accuracy) override;
+    void notifyVibratorState(int32_t deviceId, bool isOn) override;
     void notifyUntrustedTouch(const std::string& obscuringPackage) override;
     bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -918,6 +920,18 @@
     checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
 }
 
+void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyVibratorState isOn:%d", isOn);
+#endif
+    ATRACE_CALL();
+    JNIEnv* env = jniEnv();
+    ScopedLocalFrame localFrame(env);
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState,
+                        static_cast<jint>(deviceId), static_cast<jboolean>(isOn));
+    checkAndClearExceptionFromCallback(env, "notifyVibratorState");
+}
+
 void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
@@ -2248,6 +2262,8 @@
 
     GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
+
     GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
                   "(Ljava/lang/String;)V");
 
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 7ed3748..4e47984 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -77,7 +77,7 @@
     if (vibratorId < 0) {
         return std::move(std::make_unique<vibrator::HalController>());
     }
-    vibrator::ManagerHalWrapper* manager = android_server_VibratorManagerService_getManager();
+    vibrator::ManagerHalController* manager = android_server_VibratorManagerService_getManager();
     if (manager == nullptr) {
         return nullptr;
     }
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c5394f3..34f6048 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,7 @@
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_vr_VrManagerService(JNIEnv* env);
 int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env);
-int register_android_server_VibratorManagerService(JNIEnv* env);
+int register_android_server_VibratorManagerService(JavaVM* vm, JNIEnv* env);
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
 int register_android_server_tv_TvUinputBridge(JNIEnv* env);
@@ -90,7 +90,7 @@
     register_android_server_UsbHostManager(env);
     register_android_server_vr_VrManagerService(env);
     register_android_server_vibrator_VibratorController(vm, env);
-    register_android_server_VibratorManagerService(env);
+    register_android_server_VibratorManagerService(vm, env);
     register_android_server_SystemServer(env);
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_devicepolicy_CryptoTestHelper(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48f8b15..59b7367 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -136,6 +136,8 @@
     private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity";
     private static final String TAG_ORGANIZATION_ID = "organization-id";
     private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id";
+    private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS =
+            "admin-can-grant-sensors-permissions";
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
     private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -277,6 +279,7 @@
     boolean mCommonCriteriaMode;
     public String mOrganizationId;
     public String mEnrollmentSpecificId;
+    public boolean mAdminCanGrantSensorsPermissions;
 
     ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
         this.info = info;
@@ -543,6 +546,8 @@
         if (!TextUtils.isEmpty(mEnrollmentSpecificId)) {
             writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId);
         }
+        writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS,
+                mAdminCanGrantSensorsPermissions);
     }
 
     void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -792,6 +797,9 @@
                     Log.w(DevicePolicyManagerService.LOG_TAG,
                             "Missing Enrollment-specific ID.");
                 }
+            } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
+                mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
+                        false);
             } else {
                 Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
                 XmlUtils.skipCurrentTag(parser);
@@ -1143,5 +1151,8 @@
             pw.print("mEnrollmentSpecificId=");
             pw.println(mEnrollmentSpecificId);
         }
+
+        pw.print("mAdminCanGrantSensorsPermissions");
+        pw.println(mAdminCanGrantSensorsPermissions);
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 11e4db5..b52347f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
@@ -127,4 +128,10 @@
     public void provisionFullyManagedDevice(
             FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
     }
+
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
+
+    public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 8b2beb2..8ea21ec 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -46,11 +46,15 @@
     @GuardedBy("mLock")
     private final SparseIntArray mPermissionPolicy = new SparseIntArray();
 
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
+
     public void onUserRemoved(int userHandle) {
         synchronized (mLock) {
             mScreenCaptureDisabled.delete(userHandle);
             mPasswordQuality.delete(userHandle);
             mPermissionPolicy.delete(userHandle);
+            mCanGrantSensorsPermissions.delete(userHandle);
         }
     }
 
@@ -97,6 +101,21 @@
         }
     }
 
+    @Override
+    public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) {
+        synchronized (mLock) {
+            return mCanGrantSensorsPermissions.get(userHandle, false);
+        }
+    }
+
+    /** Sets ahmin control over permission grants for user. */
+    public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle,
+            boolean canGrant) {
+        synchronized (mLock) {
+            mCanGrantSensorsPermissions.put(userHandle, canGrant);
+        }
+    }
+
     /** Dump content */
     public void dump(IndentingPrintWriter pw) {
         pw.println("Device policy cache:");
@@ -104,6 +123,8 @@
         pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString());
         pw.println("Password quality: " + mPasswordQuality.toString());
         pw.println("Permission policy: " + mPermissionPolicy.toString());
+        pw.println("Admin can grant sensors permission: "
+                + mCanGrantSensorsPermissions.toString());
         pw.decreaseIndent();
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d2e5de..404b0cf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -313,6 +313,7 @@
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.Owners.OwnerDto;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.RestrictionsSet;
@@ -910,12 +911,20 @@
     };
 
     protected static class RestrictionsListener implements UserRestrictionsListener {
-        private Context mContext;
+        private final Context mContext;
+        private final UserManagerInternal mUserManagerInternal;
+        private final DevicePolicyManagerService mDpms;
 
-        public RestrictionsListener(Context context) {
+        public RestrictionsListener(
+                Context context,
+                UserManagerInternal userManagerInternal,
+                DevicePolicyManagerService dpms) {
             mContext = context;
+            mUserManagerInternal = userManagerInternal;
+            mDpms = dpms;
         }
 
+        @Override
         public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) {
             final boolean newlyDisallowed =
@@ -925,13 +934,19 @@
             final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
 
             if (restrictionChanged) {
-                // Notify ManagedProvisioning to update the built-in cross profile intent filters.
-                Intent intent = new Intent(
-                        DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-                intent.setPackage(getManagedProvisioningPackage(mContext));
-                intent.putExtra(Intent.EXTRA_USER_ID, userId);
-                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+                final int parentId = mUserManagerInternal.getProfileParentId(userId);
+                if (parentId == userId) {
+                    return;
+                }
+
+                // Always reset filters on the parent user, which handles cross profile intent
+                // filters between the parent and its profiles.
+                Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+                        + "change");
+                mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+                mContext.sendBroadcastAsUser(new Intent(
+                                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+                        UserHandle.of(userId));
             }
         }
     }
@@ -1116,6 +1131,22 @@
         mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
     }
 
+    // Used by DevicePolicyManagerServiceShellCommand
+    List<OwnerDto> listAllOwners() {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+        List<OwnerDto> owners = mOwners.listAllOwners();
+        synchronized (getLockObject()) {
+            for (int i = 0; i < owners.size(); i++) {
+                OwnerDto owner = owners.get(i);
+                owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
+            }
+        }
+
+        return owners;
+    }
+
     /**
      * Unit test will subclass it to inject mocks.
      */
@@ -1621,7 +1652,8 @@
 
         mSetupContentObserver = new SetupContentObserver(mHandler);
 
-        mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+        mUserManagerInternal.addUserRestrictionsListener(
+                new RestrictionsListener(mContext, mUserManagerInternal, this));
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         loadOwners();
@@ -2959,6 +2991,7 @@
         updatePasswordQualityCacheForUserGroup(
                 userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
         updatePermissionPolicyCache(userId);
+        updateAdminCanGrantSensorsPermissionCache(userId);
 
         startOwnerService(userId, "start-user");
     }
@@ -13451,15 +13484,15 @@
         if (!mOwners.hasDeviceOwner()) {
             return false;
         }
-        if (userId == mOwners.getDeviceOwnerUserId()) {
-            // The user that the DO is installed on is always affiliated with the device.
-            return true;
-        }
         if (userId == UserHandle.USER_SYSTEM) {
             // The system user is always affiliated in a DO device,
             // even if in headless system user mode.
             return true;
         }
+        if (userId == mOwners.getDeviceOwnerUserId()) {
+            // The user that the DO is installed on is always affiliated with the device.
+            return true;
+        }
 
         final ComponentName profileOwner = getProfileOwnerAsUser(userId);
         if (profileOwner == null) {
@@ -16008,19 +16041,9 @@
                     provisioningParams.isKeepAccountMigrated(), callerPackage);
 
             if (provisioningParams.isOrganizationOwnedProvisioning()) {
-                markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
-                restrictRemovalOfManagedProfile(admin, userInfo.id);
+                setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
             }
 
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
             return userInfo.getUserHandle();
         } catch (Exception e) {
             DevicePolicyEventLogger
@@ -16250,21 +16273,20 @@
         }
     }
 
-    private void markIsProfileOwnerOnOrganizationOwnedDevice(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+    private void setProfileOwnerOnOrgOwnedDeviceState(
+            ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+        synchronized (getLockObject()) {
+            markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+        }
+        restrictRemovalOfManagedProfile(parentUserId);
     }
 
-    private void restrictRemovalOfManagedProfile(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).addUserRestriction(
-                admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
-    }
-
-    private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
-        final Context profileContext = mContext.createContextAsUser(
-                UserHandle.of(profileId), /* flags= */ 0);
-        return profileContext.getSystemService(DevicePolicyManager.class);
+    private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+        final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+        mUserManager.setUserRestriction(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                /* value= */ true,
+                parentUserHandle);
     }
 
     @Override
@@ -16313,15 +16335,8 @@
             }
 
             disallowAddUser();
-
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+            setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(),
+                    provisioningParams.canDeviceOwnerGrantSensorsPermissions());
         } catch (Exception e) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
@@ -16420,4 +16435,76 @@
                 .setStrings(callerPackage)
                 .write();
     }
+
+    @Override
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            try {
+                final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+                final int numOfProfiles = profiles.size();
+                if (numOfProfiles <= 1) {
+                    return;
+                }
+
+                final String managedProvisioningPackageName = getManagedProvisioningPackage(
+                        mContext);
+                // Removes cross profile intent filters from the parent to all the profiles.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, mContext.getOpPackageName());
+                // Setting and resetting default cross profile intent filters was previously handled
+                // by Managed Provisioning. For backwards compatibility, clear any intent filters
+                // that were set by ManagedProvisioning.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, managedProvisioningPackageName);
+
+                // For each profile reset cross profile intent filters
+                for (int i = 0; i < numOfProfiles; i++) {
+                    UserInfo profile = profiles.get(i);
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, mContext.getOpPackageName());
+                    // Clear any intent filters that were set by ManagedProvisioning.
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, managedProvisioningPackageName);
+
+                    mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+                }
+            } catch (RemoteException e) {
+                // Shouldn't happen.
+            }
+        });
+    }
+
+    private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) {
+        synchronized (getLockObject()) {
+            ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+
+            Preconditions.checkState(
+                    isDeviceOwner(owner) && owner.getUserHandle().getIdentifier() == userId,
+                    "May only be set on a the user of a device owner.");
+
+            owner.mAdminCanGrantSensorsPermissions = canGrant;
+            mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+            saveSettingsLocked(userId);
+        }
+    }
+
+    private void updateAdminCanGrantSensorsPermissionCache(int userId) {
+        synchronized (getLockObject()) {
+            ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+            final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+            mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+        }
+    }
+
+    @Override
+    public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+        if (!mHasFeature) {
+            return false;
+        }
+
+        return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId);
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index fc1d831..222c987 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -18,13 +18,17 @@
 import android.app.admin.DevicePolicyManager;
 import android.os.ShellCommand;
 
+import com.android.server.devicepolicy.Owners.OwnerDto;
+
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.Objects;
 
 final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
 
     private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
     private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
+    private static final String CMD_LIST_OWNERS = "list-owners";
 
     private final DevicePolicyManagerService mService;
 
@@ -51,6 +55,8 @@
                     return runIsSafeOperation(pw);
                 case CMD_SET_SAFE_OPERATION:
                     return runSetSafeOperation(pw);
+                case CMD_LIST_OWNERS:
+                    return runListOwners(pw);
                 default:
                     return onInvalidCommand(pw, cmd);
             }
@@ -76,6 +82,8 @@
         pw.printf("  %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
         pw.printf("    Emulates the result of the next call to check if the given operation is safe"
                 + " \n\n");
+        pw.printf("  %s\n", CMD_LIST_OWNERS);
+        pw.printf("    Lists the device / profile owners per user \n\n");
     }
 
     private int runIsSafeOperation(PrintWriter pw) {
@@ -97,4 +105,36 @@
                 DevicePolicyManager.unsafeOperationReasonToString(reason));
         return 0;
     }
+
+    private int runListOwners(PrintWriter pw) {
+        List<OwnerDto> owners = mService.listAllOwners();
+        if (owners.isEmpty()) {
+            pw.println("none");
+            return 0;
+        }
+        int size = owners.size();
+        if (size == 1) {
+            pw.println("1 owner:");
+        } else {
+            pw.printf("%d owners:\n", size);
+        }
+
+        for (int i = 0; i < size; i++) {
+            OwnerDto owner = owners.get(i);
+            pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
+            if (owner.isDeviceOwner) {
+                pw.print(",DeviceOwner");
+            }
+            if (owner.isProfileOwner) {
+                pw.print(",ProfileOwner");
+            }
+            if (owner.isAffiliated) {
+                pw.print(",Affiliated");
+            }
+            pw.println();
+        }
+
+        return 0;
+    }
+
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 809afe0..1e70d59 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManagerInternal;
 import android.app.admin.SystemUpdateInfo;
@@ -57,6 +58,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -433,6 +435,23 @@
         }
     }
 
+    List<OwnerDto> listAllOwners() {
+        List<OwnerDto> owners = new ArrayList<>();
+        synchronized (mLock) {
+            if (mDeviceOwner != null) {
+                owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
+                        /* isDeviceOwner= */ true));
+            }
+            for (int i = 0; i < mProfileOwners.size(); i++) {
+                int userId = mProfileOwners.keyAt(i);
+                OwnerInfo info = mProfileOwners.valueAt(i);
+                owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+            }
+        }
+        return owners;
+    }
+
+
     SystemUpdatePolicy getSystemUpdatePolicy() {
         synchronized (mLock) {
             return mSystemUpdatePolicy;
@@ -1076,6 +1095,24 @@
         }
     }
 
+    /**
+     * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+     */
+    static final class OwnerDto {
+        public final @UserIdInt int userId;
+        public final ComponentName admin;
+        public final boolean isDeviceOwner;
+        public final boolean isProfileOwner;
+        public boolean isAffiliated;
+
+        private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
+            this.userId = userId;
+            this.admin = Objects.requireNonNull(admin, "admin must not be null");
+            this.isDeviceOwner = isDeviceOwner;
+            this.isProfileOwner = !isDeviceOwner;
+        }
+    }
+
     public void dump(IndentingPrintWriter pw) {
         boolean needBlank = false;
         if (mDeviceOwner != null) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index e978ed4..7534c7c 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@
     static_libs: [
         "libbase",
         "libext2_uuid",
-        "libdataloader_aidl-unstable-cpp",
-        "libincremental_aidl-unstable-cpp",
-        "libincremental_manager_aidl-unstable-cpp",
+        "libdataloader_aidl-cpp",
+        "libincremental_aidl-cpp",
+        "libincremental_manager_aidl-cpp",
         "libprotobuf-cpp-lite",
         "service.incremental.proto",
         "libutils",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 50cb00f..98c3b99 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -147,6 +147,7 @@
 import com.android.server.om.OverlayManagerService;
 import com.android.server.os.BugreportManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.people.PeopleService;
 import com.android.server.pm.BackgroundDexOptService;
@@ -185,6 +186,7 @@
 import com.android.server.testharness.TestHarnessModeService;
 import com.android.server.textclassifier.TextClassificationManagerService;
 import com.android.server.textservices.TextServicesManagerService;
+import com.android.server.tracing.TracingServiceProxy;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
@@ -207,12 +209,15 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Timer;
+import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 
@@ -481,6 +486,50 @@
 
     private static native void fdtrackAbort();
 
+    private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/");
+    private static final int MAX_HEAP_DUMPS = 2;
+
+    /**
+     * Dump system_server's heap.
+     *
+     * For privacy reasons, these aren't automatically pulled into bugreports:
+     * they must be manually pulled by the user.
+     */
+    private static void dumpHprof() {
+        // hprof dumps are rather large, so ensure we don't fill the disk by generating
+        // hundreds of these that will live forever.
+        TreeSet<File> existingTombstones = new TreeSet<>();
+        for (File file : HEAP_DUMP_PATH.listFiles()) {
+            if (!file.isFile()) {
+                continue;
+            }
+            if (!file.getName().startsWith("fdtrack-")) {
+                continue;
+            }
+            existingTombstones.add(file);
+        }
+        if (existingTombstones.size() >= MAX_HEAP_DUMPS) {
+            for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) {
+                // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place.
+                existingTombstones.pollLast();
+            }
+            for (File file : existingTombstones) {
+                if (!file.delete()) {
+                    Slog.w("System", "Failed to clean up hprof " + file);
+                }
+            }
+        }
+
+        try {
+            String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+            String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
+            Debug.dumpHprofData(filename);
+        } catch (IOException ex) {
+            Slog.e("System", "Failed to dump fdtrack hprof");
+            ex.printStackTrace();
+        }
+    }
+
     /**
      * Spawn a thread that monitors for fd leaks.
      */
@@ -505,6 +554,7 @@
                     enabled = true;
                 } else if (maxFd > abortThreshold) {
                     Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+                    dumpHprof();
                     fdtrackAbort();
                 }
 
@@ -1217,6 +1267,11 @@
         mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        // Tracks native tombstones.
+        t.traceBegin("StartNativeTombstoneManagerService");
+        mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+        t.traceEnd();
+
         // Service to capture bugreports.
         t.traceBegin("StartBugreportManagerService");
         mSystemServiceManager.startService(BugreportManagerService.class);
@@ -2429,6 +2484,11 @@
         mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
         t.traceEnd();
 
+        // Perfetto TracingServiceProxy
+        t.traceBegin("startTracingServiceProxy");
+        mSystemServiceManager.startService(TracingServiceProxy.class);
+        t.traceEnd();
+
         // It is now time to start up the app processes...
 
         t.traceBegin("MakeVibratorServiceReady");
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 d863194..c2e0b77 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
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.content.pm.PackageUserState
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.parsing.component.ParsedActivity
@@ -25,7 +26,6 @@
 import android.os.Build
 import android.os.Process
 import android.util.ArraySet
-import android.util.Singleton
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.pm.PackageSetting
@@ -46,14 +46,11 @@
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.testng.Assert.assertThrows
 import java.io.File
 import java.util.UUID
 import java.util.concurrent.atomic.AtomicBoolean
 import java.util.concurrent.atomic.AtomicInteger
 
-private typealias Enforcer = DomainVerificationEnforcer
-
 @RunWith(Parameterized::class)
 class DomainVerificationEnforcerTest {
 
@@ -64,64 +61,27 @@
         private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
         private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
 
-        private const val TEST_PKG = "com.test"
+        private const val VISIBLE_PKG = "com.test.visible"
+        private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2")
+        private const val INVISIBLE_PKG = "com.test.invisible"
+        private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3")
 
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
         fun parameters(): Array<Any> {
+            val visiblePkg = mockPkg(VISIBLE_PKG)
+            val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+            val invisiblePkg = mockPkg(INVISIBLE_PKG)
+            val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
+
             val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
-                DomainVerificationEnforcer(it)
-            }
-
-            val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
-                whenever(packageName) { TEST_PKG }
-                whenever(targetSdkVersion) { Build.VERSION_CODES.S }
-                whenever(activities) {
-                    listOf(
-                        ParsedActivity().apply {
-                            addIntent(
-                                ParsedIntentInfo().apply {
-                                    autoVerify = true
-                                    addAction(Intent.ACTION_VIEW)
-                                    addCategory(Intent.CATEGORY_BROWSABLE)
-                                    addCategory(Intent.CATEGORY_DEFAULT)
-                                    addDataScheme("https")
-                                    addDataAuthority("example.com", null)
-                                }
-                            )
+                DomainVerificationEnforcer(it).apply {
+                    setCallback(mockThrowOnUnmocked {
+                        whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+                        whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+                            true
                         }
-                    )
-                }
-            }
-
-            val uuid = UUID.randomUUID()
-
-            // TODO: PackageSetting field encapsulation to move to whenever(name)
-            val mockPkgSetting = spyThrowOnUnmocked(
-                PackageSetting(
-                    TEST_PKG,
-                    TEST_PKG,
-                    File("/test"),
-                    null,
-                    null,
-                    null,
-                    null,
-                    1,
-                    0,
-                    0,
-                    0,
-                    null,
-                    null,
-                    null,
-                    uuid
-                )
-            ) {
-                whenever(getPkg()) { mockPkg }
-                whenever(domainSetId) { uuid }
-                whenever(userState) {
-                    SparseArray<PackageUserState>().apply {
-                        this[0] = PackageUserState()
-                    }
+                    })
                 }
             }
 
@@ -129,142 +89,160 @@
                 {
                     val callingUidInt = AtomicInteger(-1)
                     val callingUserIdInt = AtomicInteger(-1)
-                    Triple(
-                        callingUidInt, callingUserIdInt, DomainVerificationService(
-                            it,
-                            mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
-                            mockThrowOnUnmocked {
-                                whenever(
-                                    isChangeEnabled(
-                                        anyLong(),
-                                        any()
-                                    )
-                                ) { true }
-                            }).apply {
-                                setConnection(mockThrowOnUnmocked {
-                                    whenever(callingUid) { callingUidInt.get() }
-                                    whenever(callingUserId) { callingUserIdInt.get() }
-                                    whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
-                                    whenever(getPackageLocked(TEST_PKG)) { mockPkg }
-                                    whenever(schedule(anyInt(), any()))
-                                    whenever(scheduleWriteSettings())
-                                })
+
+                    val connection: DomainVerificationManagerInternal.Connection =
+                        mockThrowOnUnmocked {
+                            whenever(callingUid) { callingUidInt.get() }
+                            whenever(callingUserId) { callingUserIdInt.get() }
+                            whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+                            whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+                            whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+                            whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+                            whenever(schedule(anyInt(), any()))
+                            whenever(scheduleWriteSettings())
+                            whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+                            whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+                                true
+                            }
                         }
-                    )
+                    val service = DomainVerificationService(
+                        it,
+                        mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+                        mockThrowOnUnmocked {
+                            whenever(
+                                isChangeEnabled(
+                                    anyLong(),
+                                    any()
+                                )
+                            ) { true }
+                        }).apply {
+                        setConnection(connection)
+                    }
+
+                    Triple(callingUidInt, callingUserIdInt, service)
                 }
 
             fun enforcer(
                 type: Type,
                 name: String,
-                block: DomainVerificationEnforcer.(
-                    callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
-                ) -> Unit
-            ) = Params(
-                type,
-                makeEnforcer,
-                name
-            ) { enforcer, callingUid, callingUserId, userId, proxy ->
-                enforcer.block(callingUid, callingUserId, userId, proxy)
+                block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any?
+            ) = Params(type, makeEnforcer, name) {
+                it.target.block(it)
             }
 
             fun service(
                 type: Type,
                 name: String,
-                block: DomainVerificationService.(
-                    callingUid: Int, callingUserId: Int, userId: Int
-                ) -> Unit
-            ) = Params(
-                type,
-                makeService,
-                name
-            ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
-                val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
-                callingUidInt.set(callingUid)
-                callingUserIdInt.set(callingUserId)
-                service.setProxy(proxy)
-                service.addPackage(mockPkgSetting)
-                service.block(callingUid, callingUserId, userId)
+                block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any?
+            ) = Params(type, makeService, name) {
+                val (callingUidInt, callingUserIdInt, service) = it.target
+                callingUidInt.set(it.callingUid)
+                callingUserIdInt.set(it.callingUserId)
+                service.proxy = it.proxy
+                service.addPackage(visiblePkgSetting)
+                service.addPackage(invisiblePkgSetting)
+                service.block(it)
             }
 
             return arrayOf(
-                enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
-                    assertInternal(callingUid)
+                enforcer(Type.INTERNAL, "internal") {
+                    assertInternal(it.callingUid)
                 },
-                enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
-                    assertApprovedQuerent(callingUid, proxy)
+                enforcer(Type.QUERENT, "approvedQuerent") {
+                    assertApprovedQuerent(it.callingUid, it.proxy)
                 },
-                enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
-                    assertApprovedVerifier(callingUid, proxy)
+                enforcer(Type.VERIFIER, "approvedVerifier") {
+                    assertApprovedVerifier(it.callingUid, it.proxy)
                 },
                 enforcer(
                     Type.SELECTOR,
                     "approvedUserSelector"
-                ) { callingUid, callingUserId, userId, _ ->
-                    assertApprovedUserSelector(callingUid, callingUserId, userId)
+                ) {
+                    assertApprovedUserSelector(
+                        it.callingUid, it.callingUserId,
+                        it.targetPackageName, it.userId
+                    )
                 },
-
-                service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+                service(Type.INTERNAL, "setStatusInternalPackageName") {
                     setDomainVerificationStatusInternal(
-                        TEST_PKG,
+                        it.targetPackageName,
                         DomainVerificationManager.STATE_SUCCESS,
                         ArraySet(setOf("example.com"))
                     )
                 },
-                service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+                service(Type.INTERNAL, "setUserSelectionInternal") {
                     setDomainVerificationUserSelectionInternal(
-                        userId,
-                        TEST_PKG,
+                        it.userId,
+                        it.targetPackageName,
                         false,
                         ArraySet(setOf("example.com"))
                     )
                 },
-                service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
-                    verifyPackages(listOf(TEST_PKG), true)
+                service(Type.INTERNAL, "verifyPackages") {
+                    verifyPackages(listOf(it.targetPackageName), true)
                 },
-                service(Type.INTERNAL, "clearState") { _, _, _ ->
-                    clearDomainVerificationState(listOf(TEST_PKG))
+                service(Type.INTERNAL, "clearState") {
+                    clearDomainVerificationState(listOf(it.targetPackageName))
                 },
-                service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
-                    clearUserSelections(listOf(TEST_PKG), userId)
+                service(Type.INTERNAL, "clearUserSelections") {
+                    clearUserSelections(listOf(it.targetPackageName), it.userId)
                 },
-                service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+                service(Type.VERIFIER, "getPackageNames") {
                     validVerificationPackageNames
                 },
-                service(Type.QUERENT, "getInfo") { _, _, _ ->
-                    getDomainVerificationInfo(TEST_PKG)
+                service(Type.QUERENT, "getInfo") {
+                    getDomainVerificationInfo(it.targetPackageName)
                 },
-                service(Type.VERIFIER, "setStatus") { _, _, _ ->
+                service(Type.VERIFIER, "setStatus") {
                     setDomainVerificationStatus(
-                        uuid,
+                        it.targetDomainSetId,
                         setOf("example.com"),
                         DomainVerificationManager.STATE_SUCCESS
                     )
                 },
-                service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+                service(Type.VERIFIER, "setStatusInternalUid") {
                     setDomainVerificationStatusInternal(
-                        callingUid,
-                        uuid,
+                        it.callingUid,
+                        it.targetDomainSetId,
                         setOf("example.com"),
                         DomainVerificationManager.STATE_SUCCESS
                     )
                 },
-                service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
-                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+                service(Type.SELECTOR, "setLinkHandlingAllowed") {
+                    setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
                 },
-                service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
-                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+                service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
+                    setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
                 },
-                service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
-                    getDomainVerificationUserSelection(TEST_PKG)
+                service(Type.SELECTOR, "getUserSelection") {
+                    getDomainVerificationUserSelection(it.targetPackageName)
                 },
-                service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
-                    getDomainVerificationUserSelection(TEST_PKG, userId)
+                service(Type.SELECTOR_USER, "getUserSelectionUserId") {
+                    getDomainVerificationUserSelection(it.targetPackageName, it.userId)
                 },
-                service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
-                    setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+                service(Type.SELECTOR, "setUserSelection") {
+                    setDomainVerificationUserSelection(
+                        it.targetDomainSetId,
+                        setOf("example.com"),
+                        true
+                    )
                 },
-                service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
-                    setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+                service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+                    setDomainVerificationUserSelection(
+                        it.targetDomainSetId,
+                        setOf("example.com"),
+                        true,
+                        it.userId
+                    )
+                },
+                service(Type.LEGACY_SELECTOR, "setLegacyUserState") {
+                    setLegacyUserState(
+                        it.targetPackageName, it.userId,
+                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+                    )
+                },
+                service(Type.LEGACY_QUERENT, "getLegacyUserState") {
+                    getLegacyState(it.targetPackageName, it.userId)
                 },
             )
         }
@@ -273,9 +251,7 @@
             val type: Type,
             val construct: (context: Context) -> T,
             val name: String,
-            private val method: (
-                T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
-            ) -> Unit
+            private val method: (Input<T>) -> Any?
         ) {
             override fun toString() = "${type}_$name"
 
@@ -284,10 +260,79 @@
                 callingUid: Int,
                 callingUserId: Int,
                 userId: Int,
+                targetPackageName: String,
+                targetDomainSetId: UUID,
                 proxy: DomainVerificationProxy
-            ) {
-                @Suppress("UNCHECKED_CAST")
-                method(target as T, callingUid, callingUserId, userId, proxy)
+            ): Any? = method(
+                Input(
+                    @Suppress("UNCHECKED_CAST")
+                    target as T,
+                    callingUid,
+                    callingUserId,
+                    userId,
+                    targetPackageName,
+                    targetDomainSetId,
+                    proxy
+                )
+            )
+
+            data class Input<T>(
+                val target: T,
+                val callingUid: Int,
+                val callingUserId: Int,
+                val userId: Int,
+                val targetPackageName: String,
+                val targetDomainSetId: UUID,
+                val proxy: DomainVerificationProxy
+            )
+        }
+
+        fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(this.packageName) { packageName }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+            whenever(activities) {
+                listOf(
+                    ParsedActivity().apply {
+                        addIntent(
+                            ParsedIntentInfo().apply {
+                                autoVerify = true
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("https")
+                                addDataAuthority("example.com", null)
+                            }
+                        )
+                    }
+                )
+            }
+        }
+
+        fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked(
+            PackageSetting(
+                packageName,
+                packageName,
+                File("/test"),
+                null,
+                null,
+                null,
+                null,
+                1,
+                0,
+                0,
+                0,
+                null,
+                null,
+                null,
+                domainSetId
+            )
+        ) {
+            whenever(getPkg()) { mockPkg(packageName) }
+            whenever(this.domainSetId) { domainSetId }
+            whenever(userState) {
+                SparseArray<PackageUserState>().apply {
+                    this[0] = PackageUserState()
+                }
             }
         }
     }
@@ -309,6 +354,8 @@
             Type.VERIFIER -> approvedVerifier()
             Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
             Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+            Type.LEGACY_QUERENT -> legacyQuerent()
+            Type.LEGACY_SELECTOR -> legacyUserSelector()
         }.run { /*exhaust*/ }
     }
 
@@ -316,24 +363,30 @@
         val context: Context = mockThrowOnUnmocked()
         val target = params.construct(context)
 
-        INTERNAL_UIDS.forEach { runMethod(target, it) }
-        assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
-        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+        // Internal doesn't care about visibility
+        listOf(true, false).forEach { visible ->
+            INTERNAL_UIDS.forEach { runMethod(target, it, visible) }
+            assertFails { runMethod(target, VERIFIER_UID, visible) }
+            assertFails {
+                runMethod(target, NON_VERIFIER_UID, visible)
+            }
+        }
     }
 
     fun approvedQuerent() {
         val allowUserSelection = AtomicBoolean(false)
+        val allowPreferredApps = AtomicBoolean(false)
+        val allowQueryAll = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
-            whenever(
-                enforcePermission(
-                    eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
-                    anyInt(), anyInt(), anyString()
-                )
-            ) {
-                if (!allowUserSelection.get()) {
-                    throw SecurityException()
-                }
-            }
+            initPermission(
+                allowUserSelection,
+                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+            )
+            initPermission(
+                allowPreferredApps,
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+            )
+            initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
         }
         val target = params.construct(context)
 
@@ -341,27 +394,41 @@
 
         verifyNoMoreInteractions(context)
 
+        assertFails { runMethod(target, VERIFIER_UID) }
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+        // Check that the verifier only needs QUERY_ALL to pass
+        allowQueryAll.set(true)
         runMethod(target, VERIFIER_UID)
-        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+        allowQueryAll.set(false)
+
+        allowPreferredApps.set(true)
+
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
 
         allowUserSelection.set(true)
 
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+        allowQueryAll.set(true)
+
         runMethod(target, NON_VERIFIER_UID)
     }
 
     fun approvedVerifier() {
-        val shouldThrow = AtomicBoolean(false)
+        val allowDomainVerificationAgent = AtomicBoolean(false)
+        val allowIntentVerificationAgent = AtomicBoolean(false)
+        val allowQueryAll = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
-            whenever(
-                enforcePermission(
-                    eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
-                    anyInt(), anyInt(), anyString()
-                )
-            ) {
-                if (shouldThrow.get()) {
-                    throw SecurityException()
-                }
-            }
+            initPermission(
+                allowDomainVerificationAgent,
+                android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
+            )
+            initPermission(
+                allowIntentVerificationAgent,
+                android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
+            )
+            initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
         }
         val target = params.construct(context)
 
@@ -369,94 +436,241 @@
 
         verifyNoMoreInteractions(context)
 
+        assertFails { runMethod(target, VERIFIER_UID) }
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+        allowDomainVerificationAgent.set(true)
+
+        assertFails { runMethod(target, VERIFIER_UID) }
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+        allowQueryAll.set(true)
+
         runMethod(target, VERIFIER_UID)
-        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
 
-        shouldThrow.set(true)
+        // Check that v1 verifiers are also allowed through
+        allowDomainVerificationAgent.set(false)
+        allowIntentVerificationAgent.set(true)
 
-        assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
-        assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+        runMethod(target, VERIFIER_UID)
+        assertFails { runMethod(target, NON_VERIFIER_UID) }
     }
 
     fun approvedUserSelector(verifyCrossUser: Boolean) {
-        val allowUserSelection = AtomicBoolean(true)
-        val allowInteractAcrossUsers = AtomicBoolean(true)
+        val allowUserSelection = AtomicBoolean(false)
+        val allowInteractAcrossUsers = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
-            whenever(
-                enforcePermission(
-                    eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
-                    anyInt(), anyInt(), anyString()
-                )
-            ) {
-                if (!allowUserSelection.get()) {
-                    throw SecurityException()
-                }
-            }
-            whenever(
-                enforcePermission(
-                    eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
-                    anyInt(), anyInt(), anyString()
-                )
-            ) {
-                if (!allowInteractAcrossUsers.get()) {
-                    throw SecurityException()
-                }
-            }
+            initPermission(
+                allowUserSelection,
+                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+            )
+            initPermission(
+                allowInteractAcrossUsers,
+                android.Manifest.permission.INTERACT_ACROSS_USERS
+            )
         }
         val target = params.construct(context)
 
-        fun runEachTestCaseWrapped(
-            callingUserId: Int,
-            targetUserId: Int,
-            block: (testCase: () -> Unit) -> Unit = { it.invoke() }
-        ) {
-            block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
-            block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) }
+        fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+            // User selector makes no distinction by UID
+            val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+            if (throws) {
+                allUids.forEach {
+                    assertFails {
+                        runMethod(target, it, visible = true, callingUserId, targetUserId)
+                    }
+                }
+            } else {
+                allUids.forEach {
+                    runMethod(target, it, visible = true, callingUserId, targetUserId)
+                }
+            }
+
+            // User selector doesn't use QUERY_ALL, so the invisible package should always fail
+            allUids.forEach {
+                assertFails {
+                    runMethod(target, it, visible = false, callingUserId, targetUserId)
+                }
+            }
         }
 
         val callingUserId = 0
         val notCallingUserId = 1
 
-        runEachTestCaseWrapped(callingUserId, callingUserId)
+        runTestCases(callingUserId, callingUserId, throws = true)
         if (verifyCrossUser) {
-            runEachTestCaseWrapped(callingUserId, notCallingUserId)
+            runTestCases(callingUserId, notCallingUserId, throws = true)
         }
 
-        allowInteractAcrossUsers.set(false)
+        allowUserSelection.set(true)
 
-        runEachTestCaseWrapped(callingUserId, callingUserId)
-
+        runTestCases(callingUserId, callingUserId, throws = false)
         if (verifyCrossUser) {
-            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
-                assertThrows(SecurityException::class.java, it)
-            }
-        }
-
-        allowUserSelection.set(false)
-
-        runEachTestCaseWrapped(callingUserId, callingUserId) {
-            assertThrows(SecurityException::class.java, it)
-        }
-        if (verifyCrossUser) {
-            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
-                assertThrows(SecurityException::class.java, it)
-            }
+            runTestCases(callingUserId, notCallingUserId, throws = true)
         }
 
         allowInteractAcrossUsers.set(true)
 
-        runEachTestCaseWrapped(callingUserId, callingUserId) {
-            assertThrows(SecurityException::class.java, it)
-        }
+        runTestCases(callingUserId, callingUserId, throws = false)
         if (verifyCrossUser) {
-            runEachTestCaseWrapped(callingUserId, notCallingUserId) {
-                assertThrows(SecurityException::class.java, it)
+            runTestCases(callingUserId, notCallingUserId, throws = false)
+        }
+    }
+
+    private fun legacyUserSelector() {
+        val allowInteractAcrossUsers = AtomicBoolean(false)
+        val allowPreferredApps = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            initPermission(
+                allowInteractAcrossUsers,
+                android.Manifest.permission.INTERACT_ACROSS_USERS
+            )
+            initPermission(
+                allowPreferredApps,
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+            )
+        }
+        val target = params.construct(context)
+
+        fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+            // Legacy makes no distinction by UID
+            val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+            if (throws) {
+                allUids.forEach {
+                    assertFails {
+                        runMethod(target, it, visible = true, callingUserId, targetUserId)
+                    }
+                }
+            } else {
+                allUids.forEach {
+                    runMethod(target, it, visible = true, callingUserId, targetUserId)
+                }
+            }
+
+            // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+            allUids.forEach {
+                assertFails {
+                    runMethod(target, it, visible = false, callingUserId, targetUserId)
+                }
+            }
+        }
+
+        val callingUserId = 0
+        val notCallingUserId = 1
+
+        runTestCases(callingUserId, callingUserId, throws = true)
+        runTestCases(callingUserId, notCallingUserId, throws = true)
+
+        allowPreferredApps.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        runTestCases(callingUserId, notCallingUserId, throws = true)
+
+        allowInteractAcrossUsers.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        runTestCases(callingUserId, notCallingUserId, throws = false)
+    }
+
+    private fun legacyQuerent() {
+        val allowInteractAcrossUsers = AtomicBoolean(false)
+        val allowInteractAcrossUsersFull = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            initPermission(
+                allowInteractAcrossUsers,
+                android.Manifest.permission.INTERACT_ACROSS_USERS
+            )
+            initPermission(
+                allowInteractAcrossUsersFull,
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+            )
+        }
+        val target = params.construct(context)
+
+        fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+            // Legacy makes no distinction by UID
+            val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+            if (throws) {
+                allUids.forEach {
+                    assertFails {
+                        runMethod(target, it, visible = true, callingUserId, targetUserId)
+                    }
+                }
+            } else {
+                allUids.forEach {
+                    runMethod(target, it, visible = true, callingUserId, targetUserId)
+                }
+            }
+
+            // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
+            allUids.forEach {
+                assertFails {
+                    runMethod(target, it, visible = false, callingUserId, targetUserId)
+                }
+            }
+        }
+
+        val callingUserId = 0
+        val notCallingUserId = 1
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        runTestCases(callingUserId, notCallingUserId, throws = true)
+
+        // Legacy requires the _FULL permission, so this should continue to fail
+        allowInteractAcrossUsers.set(true)
+        runTestCases(callingUserId, callingUserId, throws = false)
+        runTestCases(callingUserId, notCallingUserId, throws = true)
+
+        allowInteractAcrossUsersFull.set(true)
+        runTestCases(callingUserId, callingUserId, throws = false)
+        runTestCases(callingUserId, notCallingUserId, throws = false)
+    }
+
+    private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
+        whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
+            if (!boolean.get()) {
+                throw SecurityException()
+            }
+        }
+        whenever(checkPermission(eq(permission), anyInt(), anyInt())) {
+            if (boolean.get()) {
+                PackageManager.PERMISSION_GRANTED
+            } else {
+                PackageManager.PERMISSION_DENIED
             }
         }
     }
 
-    private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
-        params.runMethod(target, callingUid, callingUserId, userId, proxy)
+    private fun runMethod(
+        target: Any,
+        callingUid: Int,
+        visible: Boolean = true,
+        callingUserId: Int = 0,
+        userId: Int = 0
+    ): Any? {
+        val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG
+        val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID
+        return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
+    }
+
+    private fun assertFails(block: () -> Any?) {
+        try {
+            val value = block()
+            // Some methods return false rather than throwing, so check that as well
+            if ((value as? Boolean) != false) {
+                // Can also return default value if it's a legacy call
+                if ((value as? Int)
+                    != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+                ) {
+                    throw AssertionError("Expected call to return false, was $value")
+                }
+            }
+        } catch (e: SecurityException) {
+        } catch (e: PackageManager.NameNotFoundException) {
+        } catch (e: DomainVerificationManager.InvalidDomainSetException) {
+            // Any of these 3 exceptions are considered failures, which is expected
+        }
     }
 
     enum class Type {
@@ -473,6 +687,12 @@
         SELECTOR,
 
         // Holding the user setting permission, but targeting cross user
-        SELECTOR_USER
+        SELECTOR_USER,
+
+        // Legacy required no permissions except when cross-user
+        LEGACY_QUERENT,
+
+        // Holding the legacy preferred apps permission
+        LEGACY_SELECTOR
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
new file mode 100644
index 0000000..5629d1c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -0,0 +1,274 @@
+/*
+ * 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.pm.test.verify.domain
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageUserState
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.Process
+import android.util.ArraySet
+import android.util.SparseArray
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spyThrowOnUnmocked
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import java.io.File
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationSettingsMutationTest {
+
+    companion object {
+        private const val TEST_PKG = "com.test"
+
+        // Pretend to be the system. This class doesn't verify any enforcement behavior.
+        private const val TEST_UID = Process.SYSTEM_UID
+        private const val TEST_USER_ID = 10
+        private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70")
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters(): Array<Any> {
+            val context: Context = mockThrowOnUnmocked {
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+            }
+            val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+                whenever(isCallerVerifier(anyInt())) { true }
+                whenever(sendBroadcastForPackages(any()))
+            }
+
+            val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService =
+                { connection ->
+                    DomainVerificationService(
+                        context,
+                        mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+                        mockThrowOnUnmocked {
+                            whenever(isChangeEnabled(anyLong(),any())) { true }
+                        }).apply {
+                        setConnection(connection)
+                    }
+                }
+
+            fun service(name: String, block: DomainVerificationService.() -> Unit) =
+                Params(makeService, name) { service ->
+                    service.proxy = proxy
+                    service.addPackage(mockPkgSetting())
+                    service.block()
+                }
+
+            return arrayOf(
+                service("clearPackage") {
+                    clearPackage(TEST_PKG)
+                },
+                service("clearUser") {
+                    clearUser(TEST_USER_ID)
+                },
+                service("clearState") {
+                    clearDomainVerificationState(listOf(TEST_PKG))
+                },
+                service("clearUserSelections") {
+                    clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+                },
+                service("setStatus") {
+                    setDomainVerificationStatus(
+                        TEST_UUID,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service("setStatusInternalPackageName") {
+                    setDomainVerificationStatusInternal(
+                        TEST_PKG,
+                        DomainVerificationManager.STATE_SUCCESS,
+                        ArraySet(setOf("example.com"))
+                    )
+                },
+                service("setStatusInternalUid") {
+                    setDomainVerificationStatusInternal(
+                        TEST_UID,
+                        TEST_UUID,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service("setLinkHandlingAllowed") {
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+                },
+                service("setLinkHandlingAllowedUserId") {
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
+                },
+                service("setLinkHandlingAllowedInternal") {
+                    setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
+                },
+                service("setUserSelection") {
+                    setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
+                },
+                service("setUserSelectionUserId") {
+                    setDomainVerificationUserSelection(
+                        TEST_UUID,
+                        setOf("example.com"),
+                        true,
+                        TEST_USER_ID
+                    )
+                },
+                service("setUserSelectionInternal") {
+                    setDomainVerificationUserSelectionInternal(
+                        TEST_USER_ID,
+                        TEST_PKG,
+                        true,
+                        ArraySet(setOf("example.com")),
+                    )
+                },
+                service("setLegacyUserState") {
+                    setLegacyUserState(
+                        TEST_PKG,
+                        TEST_USER_ID,
+                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+                    )
+                },
+            )
+        }
+
+        data class Params(
+            val construct: (
+                DomainVerificationManagerInternal.Connection
+            ) -> DomainVerificationService,
+            val name: String,
+            val method: (DomainVerificationService) -> Unit
+        ) {
+            override fun toString() = name
+        }
+
+
+        fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { TEST_PKG }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+            whenever(activities) {
+                listOf(
+                    ParsedActivity().apply {
+                        addIntent(
+                            ParsedIntentInfo().apply {
+                                autoVerify = true
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("https")
+                                addDataAuthority("example.com", null)
+                            }
+                        )
+                    }
+                )
+            }
+        }
+
+        // TODO: PackageSetting field encapsulation to move to whenever(name)
+        fun mockPkgSetting() = spyThrowOnUnmocked(
+            PackageSetting(
+                TEST_PKG,
+                TEST_PKG,
+                File("/test"),
+                null,
+                null,
+                null,
+                null,
+                1,
+                0,
+                0,
+                0,
+                null,
+                null,
+                null,
+                TEST_UUID
+            )
+        ) {
+            whenever(getPkg()) { mockPkg() }
+            whenever(domainSetId) { TEST_UUID }
+            whenever(userState) {
+                SparseArray<PackageUserState>().apply {
+                    this[0] = PackageUserState()
+                }
+            }
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params
+
+    @Test
+    fun writeScheduled() {
+        val connection = mockConnection()
+        val service = params.construct(connection)
+        params.method(service)
+
+        verify(connection).scheduleWriteSettings()
+    }
+
+    private fun mockConnection(): DomainVerificationManagerInternal.Connection =
+        mockThrowOnUnmocked {
+            whenever(callingUid) { TEST_UID }
+            whenever(callingUserId) { TEST_USER_ID }
+            whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() }
+            whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
+            whenever(schedule(anyInt(), any()))
+            whenever(scheduleWriteSettings())
+
+            // This doesn't check for visibility; that's done in the enforcer test
+            whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+        }
+}
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
index 89abe3c..b541512 100644
--- a/services/tests/inprocesstests/AndroidTest.xml
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -24,8 +24,8 @@
     </target_preparer>
 
     <!-- Restart to clear test code from system server -->
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="teardown-command" value="am restart" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+        <option name="cleanup-action" value="REBOOT" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 7c935d5..e7d56a0 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -11,9 +11,16 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+java_defaults {
+    name: "FrameworkMockingServicesTests-jni-defaults",
+    jni_libs: [
+        "libactivitymanagermockingservicestestjni",
+    ],
+}
 
 android_test {
     name: "FrameworksMockingServicesTests",
+    defaults: ["FrameworkMockingServicesTests-jni-defaults"],
 
     srcs: ["src/**/*.java", "src/**/*.kt"],
 
@@ -72,4 +79,4 @@
     libs: [
         "android.test.runner",
     ],
-}
\ No newline at end of file
+}
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
new file mode 100644
index 0000000..928065a
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -0,0 +1,33 @@
+cc_library_shared {
+    name: "libactivitymanagermockingservicestestjni",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+
+    srcs: [
+        ":lib_cachedAppOptimizer_native",
+        "onload.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/base/libs",
+        "frameworks/native/services",
+        "system/memory/libmeminfo/include",
+    ],
+
+    shared_libs: [
+        "libandroid",
+        "libandroid_runtime",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libmeminfo",
+        "libnativehelper",
+        "libprocessgroup",
+        "libutils",
+    ],
+}
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
new file mode 100644
index 0000000..147cc47
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * this is a mini native libaray for cached app optimizer tests to run properly. It
+ * loads all the native methods necessary.
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+    register_android_server_am_CachedAppOptimizer(env);
+    return JNI_VERSION_1_4;
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index d860326..56d30cc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -97,6 +97,7 @@
 
     @Before
     public void setUp() {
+        System.loadLibrary("activitymanagermockingservicestestjni");
         mHandlerThread = new HandlerThread("");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index af4130d..ca53492 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -200,12 +200,12 @@
     }
 
     private static class DisplayModeWrapper {
-        public SurfaceControl.DisplayConfig config;
+        public SurfaceControl.DisplayMode mode;
         public float[] expectedAlternativeRefreshRates;
 
-        DisplayModeWrapper(SurfaceControl.DisplayConfig config,
+        DisplayModeWrapper(SurfaceControl.DisplayMode mode,
                 float[] expectedAlternativeRefreshRates) {
-            this.config = config;
+            this.mode = mode;
             this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates;
         }
     }
@@ -215,14 +215,15 @@
      * <code>expectedAlternativeRefreshRates</code> are present for each of the
      * <code>modes</code>.
      */
-    private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes)
+    private void testAlternativeRefreshRatesCommon(FakeDisplay display,
+                DisplayModeWrapper[] wrappedModes)
             throws InterruptedException {
         // Update the display.
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length];
-        for (int i = 0; i < modes.length; i++) {
-            configs[i] = modes[i].config;
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length];
+        for (int i = 0; i < wrappedModes.length; i++) {
+            modes[i] = wrappedModes[i].mode;
         }
-        display.configs = configs;
+        display.modes = modes;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
@@ -233,11 +234,11 @@
                 mListener.changedDisplays.get(mListener.changedDisplays.size() - 1);
         displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
         Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes;
-        assertThat(supportedModes.length).isEqualTo(configs.length);
+        assertThat(supportedModes.length).isEqualTo(modes.length);
 
-        for (int i = 0; i < modes.length; i++) {
-            assertModeIsSupported(supportedModes, configs[i],
-                    modes[i].expectedAlternativeRefreshRates);
+        for (int i = 0; i < wrappedModes.length; i++) {
+            assertModeIsSupported(supportedModes, modes[i],
+                    wrappedModes[i].expectedAlternativeRefreshRates);
         }
     }
 
@@ -251,56 +252,56 @@
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}),
+                        createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}),
+                        createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}),
+                        createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}),
+                        createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}),
+                        createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}),
         });
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}),
+                        createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}),
+                        createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}),
+                        createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}),
         });
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]),
         });
     }
 
     @Test
     public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
-        SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
-        SurfaceControl.DisplayConfig[] configs =
-                new SurfaceControl.DisplayConfig[]{displayConfig};
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
+        SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f);
+        SurfaceControl.DisplayMode[] modes =
+                new SurfaceControl.DisplayMode[]{displayMode};
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
         setUpDisplay(display);
         updateAvailableDisplays();
         mAdapter.registerLocked();
@@ -312,29 +313,29 @@
         DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
                 0).getDisplayDeviceInfoLocked();
 
-        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
 
         Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
-        assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
-                displayConfig.refreshRate)).isTrue();
+        assertThat(defaultMode.matches(displayMode.width, displayMode.height,
+                displayMode.refreshRate)).isTrue();
 
         Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
-        assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
-                displayConfig.refreshRate)).isTrue();
+        assertThat(activeMode.matches(displayMode.width, displayMode.height,
+                displayMode.refreshRate)).isTrue();
 
         // Change the display
-        SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
+        SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160,
                 60f);
-        configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
-        display.configs = configs;
-        display.activeConfig = 1;
+        modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo};
+        display.modes = modes;
+        display.activeMode = 1;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
-        assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
-        assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2);
+        assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
+        assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
         assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -343,8 +344,8 @@
         displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
         displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
 
-        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
         assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
 
         activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -358,11 +359,11 @@
 
     @Test
     public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception {
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f),
-                createFakeDisplayConfig(1920, 1080, 50f)
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f),
+                createFakeDisplayMode(1920, 1080, 50f)
         };
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, /* activeConfig */ 0);
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0);
         setUpDisplay(display);
         updateAvailableDisplays();
         mAdapter.registerLocked();
@@ -378,12 +379,12 @@
         assertThat(activeMode.matches(1920, 1080, 60f)).isTrue();
 
         // Change the display
-        display.activeConfig = 1;
+        display.activeMode = 1;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
-        assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
+        assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
         assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -471,14 +472,14 @@
     }
 
     @Test
-    public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f),
-                createFakeDisplayConfig(1920, 1080, 50f)
+    public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception {
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f),
+                createFakeDisplayMode(1920, 1080, 50f)
         };
-        final int activeConfig = 0;
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
-        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+        final int activeMode = 0;
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode);
+        display.desiredDisplayModeSpecs.defaultMode = 1;
 
         setUpDisplay(display);
         updateAvailableDisplays();
@@ -486,12 +487,12 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // Change the display
-        display.configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f)
+        display.modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f)
         };
-        // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+        // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't
         // trigger ArrayOutOfBoundsException.
-        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+        display.desiredDisplayModeSpecs.defaultMode = 1;
 
         setUpDisplay(display);
         updateAvailableDisplays();
@@ -562,13 +563,13 @@
     }
 
     private void assertModeIsSupported(Display.Mode[] supportedModes,
-            SurfaceControl.DisplayConfig mode) {
+            SurfaceControl.DisplayMode mode) {
         assertThat(Arrays.stream(supportedModes).anyMatch(
                 x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
     }
 
     private void assertModeIsSupported(Display.Mode[] supportedModes,
-            SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) {
+            SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) {
         float[] sortedAlternativeRates =
                 Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
         Arrays.sort(sortedAlternativeRates);
@@ -588,13 +589,13 @@
         public final DisplayAddress.Physical address;
         public final IBinder token = new Binder();
         public final SurfaceControl.DisplayInfo info;
-        public SurfaceControl.DisplayConfig[] configs;
-        public int activeConfig;
+        public SurfaceControl.DisplayMode[] modes;
+        public int activeMode;
         public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
         public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
                 1000, 1000, 0);
-        public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
-                new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0,
+        public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
+                new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
                     /* allowGroupSwitching */ false,
                     /* primaryRefreshRateMin */ 60.f,
                     /* primaryRefreshRateMax */ 60.f,
@@ -604,17 +605,17 @@
         private FakeDisplay(int port) {
             this.address = createDisplayAddress(port);
             this.info = createFakeDisplayInfo();
-            this.configs = new SurfaceControl.DisplayConfig[]{
-                    createFakeDisplayConfig(800, 600, 60f)
+            this.modes = new SurfaceControl.DisplayMode[]{
+                    createFakeDisplayMode(800, 600, 60f)
             };
-            this.activeConfig = 0;
+            this.activeMode = 0;
         }
 
-        private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) {
+        private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) {
             this.address = createDisplayAddress(port);
             this.info = createFakeDisplayInfo();
-            this.configs = configs;
-            this.activeConfig = activeConfig;
+            this.modes = modes;
+            this.activeMode = activeMode;
         }
     }
 
@@ -623,16 +624,16 @@
         doReturn(display.token).when(() ->
                 SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
         doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
-        doReturn(display.configs).when(
-                () -> SurfaceControl.getDisplayConfigs(display.token));
-        doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
+        doReturn(display.modes).when(
+                () -> SurfaceControl.getDisplayModes(display.token));
+        doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token));
         doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
         doReturn(display.colorModes).when(
                 () -> SurfaceControl.getDisplayColorModes(display.token));
         doReturn(display.hdrCapabilities).when(
                 () -> SurfaceControl.getHdrCapabilities(display.token));
-        doReturn(display.desiredDisplayConfigSpecs)
-                .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
+        doReturn(display.desiredDisplayModeSpecs)
+                .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token));
     }
 
     private void updateAvailableDisplays() {
@@ -655,21 +656,21 @@
         return info;
     }
 
-    private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
+    private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
             float refreshRate) {
-        return createFakeDisplayConfig(width, height, refreshRate, 0);
+        return createFakeDisplayMode(width, height, refreshRate, 0);
     }
 
-    private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
-            float refreshRate, int configGroup) {
-        final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
-        config.width = width;
-        config.height = height;
-        config.refreshRate = refreshRate;
-        config.xDpi = 100;
-        config.yDpi = 100;
-        config.configGroup = configGroup;
-        return config;
+    private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+            float refreshRate, int group) {
+        final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode();
+        mode.width = width;
+        mode.height = height;
+        mode.refreshRate = refreshRate;
+        mode.xDpi = 100;
+        mode.yDpi = 100;
+        mode.group = group;
+        return mode;
     }
 
     private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
new file mode 100644
index 0000000..35ac897
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.job;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class JobConcurrencyManagerTest {
+    private static final int UNAVAILABLE_USER = 0;
+    private JobConcurrencyManager mJobConcurrencyManager;
+    private UserManagerInternal mUserManagerInternal;
+    private ActivityManagerInternal mActivityManagerInternal;
+    private int mNextUserId;
+    private GracePeriodObserver mGracePeriodObserver;
+    private Context mContext;
+    private Resources mResources;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
+        LocalServices.addService(
+                ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    @Before
+    public void setUp() {
+        final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
+        mContext = mock(Context.class);
+        mResources = mock(Resources.class);
+        doReturn(true).when(mResources).getBoolean(
+                R.bool.config_jobSchedulerRestrictBackgroundUser);
+        when(mContext.getResources()).thenReturn(mResources);
+        doReturn(mContext).when(jobSchedulerService).getTestableContext();
+        mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
+        mGracePeriodObserver = mock(GracePeriodObserver.class);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mNextUserId = 10;
+        mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_currentUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createCurrentUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_currentProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createCurrentUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_primaryUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createPrimaryUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_primaryProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createPrimaryUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_UnexpiredUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createUnexpiredUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_UnexpiredProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createUnexpiredUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_restrictedUser() {
+        assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createRestrictedUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_restrictedProfile() {
+        assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createRestrictedUser(true))));
+    }
+
+    private UserInfo createCurrentUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createPrimaryUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(true).when(ui).isPrimary();
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createUnexpiredUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createRestrictedUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
+        doReturn(false).when(ui).isPrimary();
+        doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createNewProfile(UserInfo parent) {
+        final UserInfo ui = createNewUser();
+        parent.profileGroupId = parent.id;
+        ui.profileGroupId = parent.id;
+        doReturn(true).when(ui).isProfile();
+        return ui;
+    }
+
+    private UserInfo createNewUser() {
+        final UserInfo ui = mock(UserInfo.class);
+        ui.id = mNextUserId++;
+        doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
+        ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
+        return ui;
+    }
+
+    private static JobStatus createJob(UserInfo userInfo) {
+        JobStatus jobStatus = JobStatus.createFromJobInfo(
+                new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(),
+                userInfo.id * UserHandle.PER_USER_RANGE,
+                null, userInfo.id, "JobConcurrencyManagerTest");
+        return jobStatus;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4effa4d..f2bb47b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -62,6 +62,7 @@
 import com.android.server.PowerAllowlistInternal;
 import com.android.server.SystemServiceManager;
 import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
 
 import org.junit.After;
@@ -128,6 +129,9 @@
         // Called in DeviceIdleJobsController constructor.
         doReturn(mock(DeviceIdleInternal.class))
                 .when(() -> LocalServices.getService(DeviceIdleInternal.class));
+        // Used in JobConcurrencyManager.
+        doReturn(mock(UserManagerInternal.class))
+                .when(() -> LocalServices.getService(UserManagerInternal.class));
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
index 0311920..0597443 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
@@ -19,6 +19,8 @@
 import android.os.IPowerManager;
 import android.os.PowerManager.LocationPowerSaveMode;
 
+import com.android.server.location.eventlog.LocationEventLog;
+
 /**
  * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
  * change".
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
index c39a77e..6156ba9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
@@ -43,6 +43,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
 import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
 
 import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 8e5b16e..2822d5c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,10 @@
 
 package com.android.server.location.injector;
 
+import com.android.server.location.eventlog.LocationEventLog;
+
 public class TestInjector implements Injector {
 
-    private final LocationEventLog mLocationEventLog;
     private final FakeUserInfoHelper mUserInfoHelper;
     private final FakeAlarmHelper mAlarmHelper;
     private final FakeAppOpsHelper mAppOpsHelper;
@@ -32,14 +33,17 @@
     private final LocationUsageLogger mLocationUsageLogger;
 
     public TestInjector() {
-        mLocationEventLog = new LocationEventLog();
+        this(new LocationEventLog());
+    }
+
+    public TestInjector(LocationEventLog eventLog) {
         mUserInfoHelper = new FakeUserInfoHelper();
         mAlarmHelper = new FakeAlarmHelper();
         mAppOpsHelper = new FakeAppOpsHelper();
         mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
         mSettingsHelper = new FakeSettingsHelper();
         mAppForegroundHelper = new FakeAppForegroundHelper();
-        mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog);
+        mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
         mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
         mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
         mEmergencyHelper = new FakeEmergencyHelper();
@@ -100,9 +104,4 @@
     public LocationUsageLogger getLocationUsageLogger() {
         return mLocationUsageLogger;
     }
-
-    @Override
-    public LocationEventLog getLocationEventLog() {
-        return mLocationEventLog;
-    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 3b5cc88..66b037d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -64,6 +64,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -82,6 +83,7 @@
 
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
 import com.android.server.location.injector.FakeUserInfoHelper;
 import com.android.server.location.injector.TestInjector;
 
@@ -158,17 +160,19 @@
         doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
         doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
 
-        mInjector = new TestInjector();
+        LocationEventLog eventLog = new LocationEventLog();
+
+        mInjector = new TestInjector(eventLog);
         mInjector.getUserInfoHelper().startUser(OTHER_USER);
 
-        mPassive = new PassiveLocationProviderManager(mContext, mInjector);
+        mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog);
         mPassive.startManager();
         mPassive.setRealProvider(new PassiveLocationProvider(mContext));
 
         mProvider = new TestProvider(PROPERTIES, IDENTITY);
         mProvider.setProviderAllowed(true);
 
-        mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+        mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
         mManager.startManager();
         mManager.setRealProvider(mProvider);
     }
@@ -662,6 +666,23 @@
     }
 
     @Test
+    public void testProviderRequestListener() throws Exception {
+        IProviderRequestListener requestListener = mock(IProviderRequestListener.class);
+        mManager.addProviderRequestListener(requestListener);
+
+        ILocationListener locationListener = createMockLocationListener();
+        LocationRequest request = new LocationRequest.Builder(1).setWorkSource(
+                WORK_SOURCE).build();
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener);
+
+        verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(),
+                any(ProviderRequest.class));
+
+        mManager.unregisterLocationRequest(locationListener);
+        mManager.removeProviderRequestListener(requestListener);
+    }
+
+    @Test
     public void testGetCurrentLocation() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
         LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 6daa381..31e2b64 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,9 +59,9 @@
     },
 
     libs: [
-        "android.hardware.power-java",
+        "android.hardware.power-V1-java",
         "android.hardware.tv.cec-V1.0-java",
-        "android.hardware.vibrator-java",
+        "android.hardware.vibrator-V2-java",
         "android.hidl.manager-V1.0-java",
         "android.test.mock",
         "android.test.base",
@@ -88,7 +88,7 @@
         "libui",
         "libunwindstack",
         "libutils",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-V5-cpp",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/services/tests/servicestests/res/layout/widget_preview.xml
similarity index 74%
rename from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
rename to services/tests/servicestests/res/layout/widget_preview.xml
index e09bf7e..137ff46 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/services/tests/servicestests/res/layout/widget_preview.xml
@@ -14,7 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<resources>
-    <bool name="can_use_one_handed_bouncer">true</bool>
-</resources>
+<TextView android:id="@+id/widget_preview"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:text="Widget preview" />
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
index 6546216..72f025d 100644
--- a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
+++ b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2017 The Android Open Source Project
   ~
@@ -19,6 +20,7 @@
   android:minHeight="40dp"
   android:updatePeriodMillis="86400000"
   android:previewImage="@drawable/icon1"
+  android:previewLayout="@layout/widget_preview"
   android:resizeMode="horizontal|vertical"
   android:description="@string/widget_description"
   android:widgetCategory="home_screen">
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a03..58d6dae 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@
         assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
 
         // The constructor has the side effect of writing to file
-        new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null");
+        new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
 
         assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
     }
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index f7b2492..f0d7006 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
@@ -32,6 +33,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -44,6 +46,7 @@
 import android.hardware.input.IInputManager;
 import android.hardware.input.InputManager;
 import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorManager;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.os.CombinedVibrationEffect;
@@ -204,7 +207,7 @@
     public void createService_initializesNativeManagerServiceAndVibrators() {
         mockVibrators(1, 2);
         createService();
-        verify(mNativeWrapperMock).init();
+        verify(mNativeWrapperMock).init(any());
         assertTrue(mVibratorProviders.get(1).isInitialized());
         assertTrue(mVibratorProviders.get(2).isInitialized());
     }
@@ -557,8 +560,6 @@
     @Test
     public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
         mockVibrators(1);
-        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
-                IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         VibratorManagerService service = createService();
         // The native callback will be dispatched manually in this test.
@@ -577,6 +578,139 @@
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
     }
 
+    @Test
+    public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
+        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
+        mockVibrators(1, 2);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        VibratorManagerService service = createService();
+        // The native callback will be dispatched manually in this test.
+        mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+
+        ArgumentCaptor<VibratorManagerService.OnSyncedVibrationCompleteListener> listenerCaptor =
+                ArgumentCaptor.forClass(
+                        VibratorManagerService.OnSyncedVibrationCompleteListener.class);
+        verify(mNativeWrapperMock).init(listenerCaptor.capture());
+
+        // Mock trigger callback on registered listener.
+        when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+        when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> {
+            listenerCaptor.getValue().onComplete(answer.getArgument(0));
+            return true;
+        });
+
+        VibrationEffect composed = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+                .compose();
+        CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed);
+
+        // Wait for vibration to start, it should finish right away with trigger callback.
+        vibrate(service, effect, ALARM_ATTRS);
+
+        // VibrationThread will start this vibration async, so wait until callback is triggered.
+        assertTrue(waitUntil(s -> !listenerCaptor.getAllValues().isEmpty(), service,
+                TEST_TIMEOUT_MILLIS));
+
+        verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+        verify(mNativeWrapperMock).triggerSynced(anyLong());
+        assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects());
+        assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects());
+    }
+
+    @Test
+    public void vibrate_withMultipleVibratorsAndCapabilities_prepareAndTriggerCalled()
+            throws Exception {
+        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_PERFORM,
+                IVibratorManager.CAP_PREPARE_COMPOSE, IVibratorManager.CAP_MIXED_TRIGGER_PERFORM,
+                IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE);
+        mockVibrators(1, 2);
+        when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+        when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(true);
+        FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+        fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        VibratorManagerService service = createService();
+
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose())
+                .combine();
+        vibrate(service, effect, ALARM_ATTRS);
+        assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service,
+                TEST_TIMEOUT_MILLIS));
+
+        verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+        verify(mNativeWrapperMock).triggerSynced(anyLong());
+        verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+    }
+
+    @Test
+    public void vibrate_withMultipleVibratorsWithoutCapabilities_skipPrepareAndTrigger()
+            throws Exception {
+        // Missing CAP_MIXED_TRIGGER_ON and CAP_MIXED_TRIGGER_PERFORM.
+        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON,
+                IVibratorManager.CAP_PREPARE_PERFORM);
+        mockVibrators(1, 2);
+        FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+        fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        VibratorManagerService service = createService();
+
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+                .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+                .combine();
+        vibrate(service, effect, ALARM_ATTRS);
+        assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service,
+                TEST_TIMEOUT_MILLIS));
+
+        verify(mNativeWrapperMock, never()).prepareSynced(any());
+        verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
+        verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+    }
+
+    @Test
+    public void vibrate_withMultipleVibratorsPrepareFailed_skipTrigger() throws Exception {
+        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
+        mockVibrators(1, 2);
+        when(mNativeWrapperMock.prepareSynced(any())).thenReturn(false);
+        VibratorManagerService service = createService();
+
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.createOneShot(10, 50))
+                .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+                .combine();
+        vibrate(service, effect, ALARM_ATTRS);
+        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service,
+                TEST_TIMEOUT_MILLIS));
+
+        verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+        verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
+        verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+    }
+
+    @Test
+    public void vibrate_withMultipleVibratorsTriggerFailed_cancelPreparedSynced() throws Exception {
+        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
+        mockVibrators(1, 2);
+        when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+        when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(false);
+        VibratorManagerService service = createService();
+
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.createOneShot(10, 50))
+                .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+                .combine();
+        vibrate(service, effect, ALARM_ATTRS);
+        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service,
+                TEST_TIMEOUT_MILLIS));
+
+        verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+        verify(mNativeWrapperMock).triggerSynced(anyLong());
+        verify(mNativeWrapperMock, times(2)).cancelSynced(); // Trigger on service creation too.
+    }
 
     @Test
     public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
@@ -682,6 +816,11 @@
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
     }
 
+    private void mockCapabilities(long... capabilities) {
+        when(mNativeWrapperMock.getCapabilities()).thenReturn(
+                Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
+    }
+
     private void mockVibrators(int... vibratorIds) {
         for (int vibratorId : vibratorIds) {
             mVibratorProviders.put(vibratorId,
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 2a7905a..633957a 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -50,6 +50,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
@@ -81,6 +82,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -92,6 +94,7 @@
 @Presubmit
 public class VibratorServiceTest {
 
+    private static final int TEST_TIMEOUT_MILLIS = 1_000;
     private static final int UID = Process.ROOT_UID;
     private static final int VIBRATOR_ID = 1;
     private static final String PACKAGE_NAME = "package";
@@ -342,8 +345,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -399,8 +402,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), any(), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -409,16 +412,10 @@
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
         vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -427,26 +424,18 @@
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
         vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        // Settings callback is async, so wait before checking it never got cancelled.
+        assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
     }
 
     @Test
     public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception {
         VibratorService service = createService();
         vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
@@ -454,9 +443,8 @@
         // FakeSettingsProvider don't support testing triggering ContentObserver yet.
         service.updateVibrators();
 
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        // Settings callback is async, so wait before checking it never got cancelled.
+        assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
     }
 
     @Test
@@ -488,8 +476,8 @@
         inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -521,8 +509,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -531,18 +519,12 @@
         VibratorService service = createService();
 
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         // Trigger callbacks from controller.
         mTestLooper.moveTimeForward(50);
         mTestLooper.dispatchAll();
-
-        // VibrationThread needs some time to react to native callbacks and stop the vibrator.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -550,16 +532,10 @@
         VibratorService service = createService();
 
         vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before checking.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         service.cancelVibrate(service);
-
-        // VibrationThread will stop this vibration async, so wait before checking.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -584,17 +560,13 @@
         service.registerVibratorStateListener(mVibratorStateListenerMock);
 
         vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         service.unregisterVibratorStateListener(mVibratorStateListenerMock);
         // Trigger callbacks from controller.
         mTestLooper.moveTimeForward(50);
         mTestLooper.dispatchAll();
-        Thread.sleep(20);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
         // First notification done when listener is registered.
@@ -771,4 +743,15 @@
     private void setGlobalSetting(String settingName, int value) {
         Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
     }
+
+    private boolean waitUntil(Predicate<VibratorService> predicate,
+            VibratorService service, long timeout) throws InterruptedException {
+        long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
+        boolean predicateResult = false;
+        while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
+            Thread.sleep(10);
+            predicateResult = predicate.test(service);
+        }
+        return predicateResult;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 27edfd4..6963a1a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -110,8 +110,6 @@
         mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
 
         when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
-        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
-                true);
 
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -197,8 +195,9 @@
     }
 
     @Test
-    public void sendGesture_touchableDisplay_injectEvents()
+    public void sendGesture_touchableDevice_injectEvents()
             throws RemoteException {
+        when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -213,10 +212,9 @@
     }
 
     @Test
-    public void sendGesture_untouchableDisplay_performGestureResultFailed()
+    public void sendGesture_untouchableDevice_performGestureResultFailed()
             throws RemoteException {
-        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
-                false);
+        when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
index caa46da..d039a9d 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -18,14 +18,23 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.Manifest;
 import android.app.GameManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.HashMap;
+import java.util.function.Supplier;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -37,15 +46,88 @@
     private static final int USER_ID_1 = 1001;
     private static final int USER_ID_2 = 1002;
 
+    // Stolen from ConnectivityServiceTest.MockContext
+    class MockContext extends ContextWrapper {
+        private static final String TAG = "MockContext";
+
+        // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+        private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
+
+        MockContext(Context base) {
+            super(base);
+        }
+
+        /**
+         * Mock checks for the specified permission, and have them behave as per {@code granted}.
+         *
+         * <p>Passing null reverts to default behavior, which does a real permission check on the
+         * test package.
+         * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+         *                {@link PackageManager#PERMISSION_DENIED}.
+         */
+        public void setPermission(String permission, Integer granted) {
+            mMockedPermissions.put(permission, granted);
+        }
+
+        private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+            final Integer granted = mMockedPermissions.get(permission);
+            return granted != null ? granted : ifAbsent.get();
+        }
+
+        @Override
+        public int checkPermission(String permission, int pid, int uid) {
+            return checkMockedPermission(
+                    permission, () -> super.checkPermission(permission, pid, uid));
+        }
+
+        @Override
+        public int checkCallingOrSelfPermission(String permission) {
+            return checkMockedPermission(
+                    permission, () -> super.checkCallingOrSelfPermission(permission));
+        }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            final Integer granted = mMockedPermissions.get(permission);
+            if (granted == null) {
+                super.enforceCallingOrSelfPermission(permission, message);
+                return;
+            }
+
+            if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
+                throw new SecurityException("[Test] permission denied: " + permission);
+            }
+        }
+    }
+
+    @Mock
+    private MockContext mMockContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockContext = new MockContext(InstrumentationRegistry.getContext());
+    }
+
+    private void mockModifyGameModeGranted() {
+        mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+                PackageManager.PERMISSION_GRANTED);
+    }
+
+    private void mockModifyGameModeDenied() {
+        mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+                PackageManager.PERMISSION_DENIED);
+    }
+
     /**
      * By default game mode is not supported.
      */
     @Test
     public void testGameModeDefaultValue() {
-        GameManagerService gameManagerService =
-                new GameManagerService(InstrumentationRegistry.getContext());
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
         gameManagerService.onUserStarting(USER_ID_1);
 
+        mockModifyGameModeGranted();
+
         assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
                 gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1));
     }
@@ -55,10 +137,11 @@
      */
     @Test
     public void testDefaultValueForNonexistentUser() {
-        GameManagerService gameManagerService =
-                new GameManagerService(InstrumentationRegistry.getContext());
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
         gameManagerService.onUserStarting(USER_ID_1);
 
+        mockModifyGameModeGranted();
+
         gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2);
         assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
                 gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2));
@@ -69,10 +152,11 @@
      */
     @Test
     public void testGameMode() {
-        GameManagerService gameManagerService =
-                new GameManagerService(InstrumentationRegistry.getContext());
+        GameManagerService gameManagerService =  new GameManagerService(mMockContext);
         gameManagerService.onUserStarting(USER_ID_1);
 
+        mockModifyGameModeGranted();
+
         assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
                 gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
         gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -83,4 +167,48 @@
         assertEquals(GameManager.GAME_MODE_PERFORMANCE,
                 gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
     }
+
+    /**
+     * Test permission.MANAGE_GAME_MODE is checked
+     */
+    @Test
+    public void testGetGameModePermissionDenied() {
+        GameManagerService gameManagerService =  new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+
+        // Update the game mode so we can read back something valid.
+        mockModifyGameModeGranted();
+        gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+        // Deny permission.MANAGE_GAME_MODE and verify we get back GameManager.GAME_MODE_UNSUPPORTED
+        mockModifyGameModeDenied();
+        assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+                gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+    }
+
+    /**
+     * Test permission.MANAGE_GAME_MODE is checked
+     */
+    @Test
+    public void testSetGameModePermissionDenied() {
+        GameManagerService gameManagerService =  new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+
+        // Update the game mode so we can read back something valid.
+        mockModifyGameModeGranted();
+        gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+        // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+        mockModifyGameModeDenied();
+        gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE,
+                USER_ID_1);
+
+        mockModifyGameModeGranted();
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 96f4344..78eb2df 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.appwidget;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -44,6 +46,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.RemoteViews;
 
+import com.android.frameworks.servicestests.R;
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.server.LocalServices;
 
@@ -293,6 +296,13 @@
         }
     }
 
+    public void testGetPreviewLayout() {
+        AppWidgetProviderInfo info =
+                mManager.getInstalledProvidersForPackage(mPkgName, null).get(0);
+
+        assertThat(info.previewLayout).isEqualTo(R.layout.widget_preview);
+    }
+
     private int setupHostAndWidget() {
         List<PendingHostUpdate> updates = mService.startListening(
                 mMockHost, mPkgName, HOST_ID, new int[0]).getList();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 8d890b9c..7a4b901 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -103,7 +103,8 @@
     @Test
     public void testNewAuthSession_eligibleSensorsSetToStateUnknown() throws RemoteException {
         setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
-        setupFace(1 /* id */, false /* confirmationAlwaysRequired */);
+        setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+                mock(IBiometricAuthenticator.class));
 
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
@@ -118,7 +119,8 @@
 
     @Test
     public void testStartNewAuthSession() throws RemoteException {
-        setupFace(0 /* id */, false /* confirmationAlwaysRequired */);
+        setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
+                mock(IBiometricAuthenticator.class));
         setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_REAR);
 
         final boolean requireConfirmation = true;
@@ -181,9 +183,6 @@
 
         final long operationId = 123;
         final int userId = 10;
-        final int callingUid = 100;
-        final int callingPid = 1000;
-        final int callingUserId = 10000;
 
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
@@ -220,7 +219,25 @@
         assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
         assertEquals(BiometricSensor.STATE_AUTHENTICATING,
                 session.mPreAuthInfo.eligibleSensors.get(0).getSensorState());
+    }
 
+    @Test
+    public void testCancelAuthentication_whenStateAuthCalled_invokesCancel()
+            throws RemoteException {
+        final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+        setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                0 /* operationId */,
+                0 /* userId */);
+
+        session.goToInitialState();
+        assertEquals(STATE_AUTH_CALLED, session.getState());
+        session.onCancelAuthSession(false /* force */);
+
+        verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE));
     }
 
     private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId,
@@ -282,14 +299,14 @@
                 false /* resetLockoutRequiresHardwareAuthToken */));
     }
 
-    private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException {
-        IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
-        when(faceAuthenticator.isHardwareDetected(any())).thenReturn(true);
-        when(faceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+    private void setupFace(int id, boolean confirmationAlwaysRequired,
+            IBiometricAuthenticator authenticator) throws RemoteException {
+        when(authenticator.isHardwareDetected(any())).thenReturn(true);
+        when(authenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
         mSensors.add(new BiometricSensor(id,
                 TYPE_FACE /* modality */,
                 Authenticators.BIOMETRIC_STRONG /* strength */,
-                faceAuthenticator) {
+                authenticator) {
             @Override
             boolean confirmationAlwaysRequired(int userId) {
                 return confirmationAlwaysRequired;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 2d45726..7dd0734 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -27,6 +27,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
 
 import android.content.Context;
 import android.hardware.biometrics.BiometricConstants;
@@ -159,8 +161,8 @@
 
         // Client 1 cleans up properly
         verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(),
-                eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0));
-        verify(callback1).onClientFinished(eq(client1), eq(true) /* success */);
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0));
+        verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
         verify(callback1, never()).onClientStarted(any());
 
         // Client 2 was able to start
@@ -310,6 +312,37 @@
         assertNull(mScheduler.getCurrentClient());
     }
 
+    @Test
+    public void testInterruptPrecedingClients_whenExpected() {
+        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
+                withSettings().extraInterfaces(Interruptable.class));
+
+        final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+        when(interrupter.interruptsPrecedingClients()).thenReturn(true);
+
+        mScheduler.scheduleClientMonitor(interruptableMonitor);
+        mScheduler.scheduleClientMonitor(interrupter);
+        waitForIdle();
+
+        verify((Interruptable) interruptableMonitor).cancel();
+        mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */);
+    }
+
+    @Test
+    public void testDoesNotInterruptPrecedingClients_whenNotExpected() {
+        final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
+                withSettings().extraInterfaces(Interruptable.class));
+
+        final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+        when(interrupter.interruptsPrecedingClients()).thenReturn(false);
+
+        mScheduler.scheduleClientMonitor(interruptableMonitor);
+        mScheduler.scheduleClientMonitor(interrupter);
+        waitForIdle();
+
+        verify((Interruptable) interruptableMonitor, never()).cancel();
+    }
+
     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
     public void testDisallowSharingIntoProfileSetRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileClearRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileUnchanged() {
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
         verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
     }
 
-    private void verifyDataSharingChangedBroadcast() {
+    private void verifyDataSharingAppliedBroadcast() {
         Intent expectedIntent = new Intent(
-                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-        expectedIntent.setPackage("com.android.managedprovisioning");
-        expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(expectedIntent),
-                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 54da643..1a22661 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -19,10 +19,14 @@
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.devicestate.DeviceStateRequest;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
@@ -33,6 +37,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.HashMap;
 import java.util.Optional;
 
 import javax.annotation.Nullable;
@@ -61,87 +66,69 @@
     }
 
     @Test
-    public void requestStateChange() {
+    public void baseStateChanged() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
     }
 
     @Test
-    public void requestStateChange_pendingState() {
+    public void baseStateChanged_withStatePendingPolicyCallback() {
         mPolicy.blockConfigure();
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+        mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
-    public void requestStateChange_unsupportedState() {
-        mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+    public void baseStateChanged_unsupportedState() {
+        assertThrows(IllegalArgumentException.class, () -> {
+            mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+        });
+
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
     @Test
-    public void requestStateChange_invalidState() {
+    public void baseStateChanged_invalidState() {
         assertThrows(IllegalArgumentException.class, () -> {
-            mProvider.notifyRequestState(INVALID_DEVICE_STATE);
+            mProvider.setState(INVALID_DEVICE_STATE);
         });
-    }
 
-    @Test
-    public void requestOverrideState() {
-        mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
-        // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
-
-        // Committed state is set back to the requested state once the override is cleared.
-        mService.clearOverrideState();
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
-    }
-
-    @Test
-    public void requestOverrideState_unsupportedState() {
-        mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
-        // Committed state remains the same as the override state is unsupported.
-        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
@@ -150,7 +137,7 @@
     public void supportedStatesChanged() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
 
@@ -158,46 +145,27 @@
         // supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
     }
 
     @Test
-    public void supportedStatesChanged_unsupportedRequestedState() {
+    public void supportedStatesChanged_unsupportedBaseState() {
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
 
         // The current requested state is cleared because it is no longer supported.
         assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState(), Optional.empty());
+        assertEquals(mService.getBaseState(), Optional.empty());
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
 
         assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
         assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
-    }
-
-    @Test
-    public void supportedStatesChanged_unsupportedOverrideState() {
-        mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
-        // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
-
-        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
-
-        // Committed state is set back to the requested state as the override state is no longer
-        // supported.
-        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
-        assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
     }
 
     @Test
@@ -205,17 +173,17 @@
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         assertNotNull(callback.getLastNotifiedValue());
         assertEquals(callback.getLastNotifiedValue().intValue(),
                 OTHER_DEVICE_STATE.getIdentifier());
 
-        mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+        mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(callback.getLastNotifiedValue().intValue(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.blockConfigure();
-        mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
         // The callback should not have been notified of the state change as the policy is still
         // pending callback.
         assertEquals(callback.getLastNotifiedValue().intValue(),
@@ -237,6 +205,148 @@
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
+    @Test
+    public void getSupportedDeviceStates() throws RemoteException {
+        final int[] expectedStates = new int[] { 0, 1 };
+        assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
+    }
+
+    @Test
+    public void requestState() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
+        final IBinder token = new Binder();
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+                0 /* flags */);
+
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        // Committed state changes as there is a requested override.
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
+
+
+        mService.getBinderService().cancelRequest(token);
+
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        // Committed state is set back to the requested state once the override is cleared.
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertFalse(mService.getOverrideState().isPresent());
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
+    }
+
+    @Test
+    public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
+        final IBinder token = new Binder();
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+                DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
+
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+
+        // Committed state changes as there is a requested override.
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
+
+        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+
+        // Request is canceled because the base state changed.
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        // Committed state is set back to the requested state once the override is cleared.
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+        assertFalse(mService.getOverrideState().isPresent());
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
+    }
+
+    @Test
+    public void requestState_becomesUnsupported() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
+        final IBinder token = new Binder();
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+                0 /* flags */);
+
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        // Committed state changes as there is a requested override.
+        assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                OTHER_DEVICE_STATE.getIdentifier());
+
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+
+        // Request is canceled because the state is no longer supported.
+        assertEquals(callback.getLastNotifiedStatus(token),
+                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        // Committed state is set back to the requested state as the override state is no longer
+        // supported.
+        assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+        assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+        assertFalse(mService.getOverrideState().isPresent());
+        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+                DEFAULT_DEVICE_STATE.getIdentifier());
+    }
+
+    @Test
+    public void requestState_unsupportedState() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            final IBinder token = new Binder();
+            mService.getBinderService().requestState(token,
+                    UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+        });
+    }
+
+    @Test
+    public void requestState_invalidState() throws RemoteException {
+        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        mService.getBinderService().registerCallback(callback);
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            final IBinder token = new Binder();
+            mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */);
+        });
+    }
+
+    @Test
+    public void requestState_beforeRegisteringCallback() {
+        assertThrows(IllegalStateException.class, () -> {
+            final IBinder token = new Binder();
+            mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
+                    0 /* flags */);
+        });
+    }
+
     private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
         private final DeviceStateProvider mProvider;
         private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -306,23 +416,48 @@
             mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
         }
 
-        public void notifyRequestState(int identifier) {
+        public void setState(int identifier) {
             mListener.onStateChanged(identifier);
         }
     }
 
     private static final class TestDeviceStateManagerCallback extends
             IDeviceStateManagerCallback.Stub {
-        Integer mLastNotifiedValue;
+        public static final int STATUS_UNKNOWN = 0;
+        public static final int STATUS_ACTIVE = 1;
+        public static final int STATUS_SUSPENDED = 2;
+        public static final int STATUS_CANCELED = 3;
+
+        private Integer mLastNotifiedValue;
+        private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
 
         @Override
         public void onDeviceStateChanged(int deviceState) {
             mLastNotifiedValue = deviceState;
         }
 
+        @Override
+        public void onRequestActive(IBinder token) {
+            mLastNotifiedStatus.put(token, STATUS_ACTIVE);
+        }
+
+        @Override
+        public void onRequestSuspended(IBinder token) {
+            mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
+        }
+
+        @Override
+        public void onRequestCanceled(IBinder token) {
+            mLastNotifiedStatus.put(token, STATUS_CANCELED);
+        }
+
         @Nullable
         Integer getLastNotifiedValue() {
             return mLastNotifiedValue;
         }
+
+        int getLastNotifiedStatus(IBinder requestToken) {
+            return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
new file mode 100644
index 0000000..1915b8c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.job;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class GracePeriodObserverTest {
+    private GracePeriodObserver mGracePeriodObserver;
+    private UserManagerInternal mUserManagerInternal;
+    private static final int FIRST_USER = 0;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        UserManagerInternal userManagerInternal = mock(UserManagerInternal.class);
+        LocalServices.addService(UserManagerInternal.class, userManagerInternal);
+        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    @Before
+    public void setUp() {
+        final Context context = ApplicationProvider.getApplicationContext();
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+        doReturn(FIRST_USER)
+                .when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId();
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        doReturn(true).when(mUserManagerInternal).exists(FIRST_USER);
+        mGracePeriodObserver = new GracePeriodObserver(context);
+    }
+
+    @Test
+    public void testGracePeriod() throws RemoteException {
+        final int oldUser = FIRST_USER;
+        final int newUser = 10;
+        doReturn(true).when(mUserManagerInternal).exists(newUser);
+        mGracePeriodObserver.onUserSwitchComplete(newUser);
+        assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.offset(JobSchedulerService.sElapsedRealtimeClock,
+                        Duration.ofMillis(mGracePeriodObserver.mGracePeriod));
+        assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+    }
+
+    @Test
+    public void testCleanUp() throws RemoteException {
+        final int removedUser = FIRST_USER;
+        final int newUser = 10;
+        mGracePeriodObserver.onUserSwitchComplete(newUser);
+
+        final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size();
+        doReturn(false).when(mUserManagerInternal).exists(removedUser);
+
+        mGracePeriodObserver.onUserRemoved(removedUser);
+        assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 263cf48..353ac4b 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
 
@@ -47,6 +50,10 @@
 public class WorkCountTrackerTest {
     private static final String TAG = "WorkerCountTrackerTest";
 
+    private static final double[] EQUAL_PROBABILITY_CDF =
+            buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
+                    1.0 / NUM_WORK_TYPES);
+
     private Random mRandom;
     private WorkCountTracker mWorkCountTracker;
 
@@ -56,6 +63,47 @@
         mWorkCountTracker = new WorkCountTracker();
     }
 
+    @NonNull
+    private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) {
+        double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES];
+        double sum = 0;
+
+        sum += pTop;
+        cdf[0] = sum;
+        sum += pEj;
+        cdf[1] = sum;
+        sum += pBg;
+        cdf[2] = sum;
+        sum += pBgUser;
+        cdf[3] = sum;
+
+        if (Double.compare(1, sum) != 0) {
+            throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+        }
+        return cdf;
+    }
+
+    @JobConcurrencyManager.WorkType
+    static int getRandomWorkType(double[] cdf, double rand) {
+        for (int i = cdf.length - 1; i >= 0; --i) {
+            if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
+                switch (i) {
+                    case 0:
+                        return WORK_TYPE_TOP;
+                    case 1:
+                        return WORK_TYPE_EJ;
+                    case 2:
+                        return WORK_TYPE_BG;
+                    case 3:
+                        return WORK_TYPE_BGUSER;
+                    default:
+                        throw new IllegalStateException("Unknown work type");
+                }
+            }
+        }
+        throw new IllegalStateException("Couldn't pick random work type");
+    }
+
     /**
      * Represents running and pending jobs.
      */
@@ -63,25 +111,22 @@
         public final SparseIntArray running = new SparseIntArray();
         public final SparseIntArray pending = new SparseIntArray();
 
-        public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
-            while (mRandom.nextDouble() < startRatio) {
-                if (mRandom.nextDouble() < fgJobRatio) {
-                    pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
-                } else {
-                    pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
-                }
+        public void maybeEnqueueJobs(double probStart, double[] typeCdf) {
+            while (mRandom.nextDouble() < probStart) {
+                final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble());
+                pending.put(workType, pending.get(workType) + 1);
             }
         }
 
-        public void maybeFinishJobs(double stopRatio) {
+        public void maybeFinishJobs(double probStop) {
             for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
-                if (mRandom.nextDouble() < stopRatio) {
+                if (mRandom.nextDouble() < probStop) {
                     running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
                     mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
                 }
             }
             for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
-                if (mRandom.nextDouble() < stopRatio) {
+                if (mRandom.nextDouble() < probStop) {
                     running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
                     mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
                 }
@@ -116,29 +161,27 @@
         mWorkCountTracker.onCountDone();
     }
 
-    private void startPendingJobs(Jobs jobs) {
-        while ((jobs.pending.get(WORK_TYPE_TOP) > 0
-                && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
-                || (jobs.pending.get(WORK_TYPE_BG) > 0
-                && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
-            final boolean isStartingFg = mRandom.nextBoolean();
+    private boolean hasStartablePendingJob(Jobs jobs) {
+        for (int i = 0; i < jobs.pending.size(); ++i) {
+            if (jobs.pending.valueAt(i) > 0
+                    && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-            if (isStartingFg) {
-                if (jobs.pending.get(WORK_TYPE_TOP) > 0
-                        && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
-                    jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
-                    jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
-                    mWorkCountTracker.stageJob(WORK_TYPE_TOP);
-                    mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
-                }
-            } else {
-                if (jobs.pending.get(WORK_TYPE_BG) > 0
-                        && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
-                    jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
-                    jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
-                    mWorkCountTracker.stageJob(WORK_TYPE_BG);
-                    mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
-                }
+    private void startPendingJobs(Jobs jobs) {
+        while (hasStartablePendingJob(jobs)) {
+            final int startingWorkType =
+                    getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+            if (jobs.pending.get(startingWorkType) > 0
+                    && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
+                jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1);
+                jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
+                mWorkCountTracker.stageJob(startingWorkType);
+                mWorkCountTracker.onJobStarted(startingWorkType);
             }
         }
     }
@@ -149,10 +192,10 @@
     private void checkRandom(Jobs jobs, int numTests, int totalMax,
             @NonNull List<Pair<Integer, Integer>> minLimits,
             @NonNull List<Pair<Integer, Integer>> maxLimits,
-            double startRatio, double fgJobRatio, double stopRatio) {
+            double probStart, double[] typeCdf, double probStop) {
         for (int i = 0; i < numTests; i++) {
-            jobs.maybeFinishJobs(stopRatio);
-            jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
+            jobs.maybeFinishJobs(probStop);
+            jobs.maybeEnqueueJobs(probStart, typeCdf);
 
             recount(jobs, totalMax, minLimits, maxLimits);
             startPendingJobs(jobs);
@@ -178,18 +221,19 @@
      */
     @Test
     public void testRandom1() {
+        assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
         final Jobs jobs = new Jobs();
 
         final int numTests = 5000;
         final int totalMax = 6;
         final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.1;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.1;
+        final double probStop = 0.1;
+        final double probStart = 0.1;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                EQUAL_PROBABILITY_CDF, probStop);
     }
 
     @Test
@@ -198,14 +242,14 @@
 
         final int numTests = 5000;
         final int totalMax = 2;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.5, 0, 0.5, 0);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -214,14 +258,14 @@
 
         final int numTests = 5000;
         final int totalMax = 2;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -230,14 +274,14 @@
 
         final int numTests = 5000;
         final int totalMax = 10;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -246,14 +290,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.1;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.1, 0, 0.8, .1);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -262,14 +306,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.9;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.9, 0, 0.1, 0);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -278,14 +322,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.4;
-        final double fgJobRatio = 0.1;
-        final double startRatio = 0.5;
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.1, 0, 0.1, .8);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -294,14 +338,136 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
-        final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.4;
-        final double fgJobRatio = 0.9;
-        final double startRatio = 0.5;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom9() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.5, 0.5);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom10() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.1, 0.9);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom11() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.9, 0.1);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom12() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.5, 0.5, 0, 0);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom13() {
+        assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 13;
+        final List<Pair<Integer, Integer>> maxLimits = List.of(
+                Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+                Pair.create(WORK_TYPE_BGUSER, 3));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
+        final double probStop = 0.01;
+        final double probStart = 0.99;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                EQUAL_PROBABILITY_CDF, probStop);
+    }
+
+    @Test
+    public void testRandom14() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom15() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+                        Pair.create(WORK_TYPE_BGUSER, 1));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     /** Used by the following tests */
@@ -337,7 +503,7 @@
     public void testBasic() {
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
@@ -345,7 +511,7 @@
 
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
@@ -354,7 +520,7 @@
         // When there are BG jobs pending, 2 (min-BG) jobs should run.
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
@@ -385,6 +551,16 @@
 
         checkSimple(8,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+                /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49))
+        );
+
+
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
                 /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
                 /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
@@ -407,6 +583,14 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
 
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
                 /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
@@ -415,6 +599,16 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
 
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5),
+                        Pair.create(WORK_TYPE_BG, 3)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BG, 3)));
+
         // This could happen if we lower the effective config due to higher memory pressure after
         // we've already started running jobs. We shouldn't stop already running jobs, but also
         // shouldn't start new ones.
@@ -425,6 +619,38 @@
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10),
+                        Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+                        Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BGUSER, 3)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* resPen */ List.of(
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* resPen */ List.of(
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
     }
 
     /** Tests that the counter updates properly when jobs are stopped. */
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index c28292f..2288a89 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -16,8 +16,11 @@
 package com.android.server.job;
 
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
@@ -31,11 +34,9 @@
 import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
@@ -43,9 +44,13 @@
 public class WorkTypeConfigTest {
     private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
     private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+    private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
     private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+    private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
     private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+    private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
     private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+    private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
 
     @After
     public void tearDown() throws Exception {
@@ -56,43 +61,27 @@
         // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
     }
 
     private void check(@Nullable DeviceConfig.Properties config,
             int defaultTotal,
-            @Nullable Pair<Integer, Integer> defaultTopLimits,
-            @Nullable Pair<Integer, Integer> defaultBgLimits,
+            @NonNull List<Pair<Integer, Integer>> defaultMin,
+            @NonNull List<Pair<Integer, Integer>> defaultMax,
             boolean expectedValid, int expectedTotal,
-            @NonNull Pair<Integer, Integer> expectedTopLimits,
-            @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
+            @NonNull List<Pair<Integer, Integer>> expectedMinLimits,
+            @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
         resetConfig();
         if (config != null) {
             DeviceConfig.setProperties(config);
         }
 
-        List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
-        List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
-        Integer val;
-        if (defaultTopLimits != null) {
-            if ((val = defaultTopLimits.first) != null) {
-                defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
-            }
-            if ((val = defaultTopLimits.second) != null) {
-                defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
-            }
-        }
-        if (defaultBgLimits != null) {
-            if ((val = defaultBgLimits.first) != null) {
-                defaultMin.add(Pair.create(WORK_TYPE_BG, val));
-            }
-            if ((val = defaultBgLimits.second) != null) {
-                defaultMax.add(Pair.create(WORK_TYPE_BG, val));
-            }
-        }
-
         final WorkTypeConfig counts;
         try {
             counts = new WorkTypeConfig("test",
@@ -112,40 +101,126 @@
 
         counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
 
-        Assert.assertEquals(expectedTotal, counts.getMaxTotal());
-        Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
-        Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
-        Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
-        Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
+        assertEquals(expectedTotal, counts.getMaxTotal());
+        for (Pair<Integer, Integer> min : expectedMinLimits) {
+            assertEquals((int) min.second, counts.getMinReserved(min.first));
+        }
+        for (Pair<Integer, Integer> max : expectedMaxLimits) {
+            assertEquals((int) max.second, counts.getMax(max.first));
+        }
     }
 
     @Test
     public void test() throws Exception {
         // Tests with various combinations.
-        check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
-                /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
-        check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
-                /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
-        check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
-                /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
-        check(null, /*default*/ -1, null, Pair.create(-1, -1),
-                /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
-        check(null, /*default*/ 5, null, Pair.create(5, 5),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
-        check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
-                /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
-        check(null, /*default*/ 4, null, Pair.create(6, 5),
-                /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
-        check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
-                /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
-        check(null, /*default*/ 15, null, Pair.create(15, 15),
-                /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
-        check(null, /*default*/ 16, null, Pair.create(16, 16),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
-        check(null, /*default*/ 20, null, Pair.create(20, 20),
-                /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
-        check(null, /*default*/ 20, null, Pair.create(16, 16),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+        check(null, /*default*/ 13,
+                /* min */ List.of(),
+                /* max */ List.of(),
+                /*expected*/ true, 13,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13),
+                        Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+        check(null, /*default*/ 0,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)),
+                /*expected*/ false, 1,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ -1,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+                /*expected*/ false, 1,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)));
+        check(null, /*default*/ 6,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /*expected*/ false, 6,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)));
+        check(null, /*default*/ 4,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+                /*expected*/ false, 4,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 10,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                        Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 10,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                        Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 15,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+                /*expected*/ true, 15,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15)));
+        check(null, /*default*/ 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+        check(null, /*default*/ 20,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)),
+                /*expected*/ false, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16),
+                        Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16)));
+        check(null, /*default*/ 20,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
 
         // Test for overriding with a setting string.
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -153,26 +228,66 @@
                         .setInt(KEY_MAX_BG, 4)
                         .setInt(KEY_MIN_BG, 3)
                         .build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)));
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_TOTAL, 5).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5)));
+
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_BG, 4).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4)));
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MIN_BG, 3).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9)));
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt(KEY_MAX_TOTAL, 20)
+                        .setInt(KEY_MAX_EJ, 5)
+                        .setInt(KEY_MIN_EJ, 2)
+                        .setInt(KEY_MAX_BG, 16)
+                        .setInt(KEY_MIN_BG, 8)
+                        .build(),
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2),
+                        Pair.create(WORK_TYPE_BG, 8)),
+                /* max */
+                List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5),
+                        Pair.create(WORK_TYPE_BG, 16)));
+
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_TOTAL, 20)
                         .setInt(KEY_MAX_BG, 20)
                         .setInt(KEY_MIN_BG, 8)
                         .build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
     }
 }
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 58ba907..3ebe4ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -19,11 +19,13 @@
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -112,8 +114,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
@@ -1985,13 +1985,6 @@
         return users;
     }
 
-    private NetworkInfo buildNetworkInfo() {
-        final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_LTE, null, null);
-        ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-        return ni;
-    }
-
     private LinkProperties buildLinkProperties(String iface) {
         final LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(iface);
@@ -2045,13 +2038,12 @@
     }
 
     private static NetworkState buildWifi() {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addTransportType(TRANSPORT_WIFI);
         networkCapabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID);
     }
 
     private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2072,7 +2064,7 @@
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(buildNetworkInfo(),
+                new NetworkState(TYPE_MOBILE,
                         buildLinkProperties(TEST_IFACE),
                         buildNetworkCapabilities(TEST_SUB_ID, roaming),
                         new Network(TEST_NET_ID), TEST_IMSI, null)
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+    @Test
+    public void testIsEmpty() {
+        assertThat(BundleUtils.isEmpty(null)).isTrue();
+        assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+        assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+    }
+
+    @Test
+    public void testClone() {
+        Bundle in = new Bundle();
+        Bundle out = BundleUtils.clone(in);
+        assertThat(in).isNotSameInstanceAs(out);
+        assertRestrictions(out, new Bundle());
+
+        out = BundleUtils.clone(null);
+        assertThat(out).isNotNull();
+        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 9ba0967..7b9a00d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -71,12 +71,12 @@
     @Before
     public void setUp() {
         mIncrementalStates = new IncrementalStates();
-        assertFalse(mIncrementalStates.isStartable());
+        assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable());
         mIncrementalStates.setCallback(mCallback);
         mIncrementalStates.onCommit(true);
         // Test that package is now startable and loading
-        assertTrue(mIncrementalStates.isStartable());
-        assertTrue(mIncrementalStates.isLoading());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading());
         mUnstartableCalled.close();
         mFullyLoadedCalled.close();
     }
@@ -90,7 +90,7 @@
                 IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
         // Test that package is still startable
         assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
         assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
     }
 
@@ -104,7 +104,7 @@
                 IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
         // Test that package is still startable
         assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
     }
 
     /**
@@ -116,7 +116,7 @@
                 IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
         // Test that package is still startable
         assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
         assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
                 mUnstartableReason.get());
     }
@@ -130,7 +130,7 @@
                 IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
         // Test that package is still startable
         assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
         assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
                 mUnstartableReason.get());
     }
@@ -145,12 +145,12 @@
         mIncrementalStates.setProgress(1.0f);
         // Test that package is now fully loaded
         assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertFalse(mIncrementalStates.isLoading());
+        assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading());
         mIncrementalStates.onStorageHealthStatusChanged(
                 IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
         // Test that package is still startable
         assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
     }
 
     /**
@@ -159,6 +159,6 @@
     @Test
     public void testStartableTransition_AppCrashOrAnr() {
         mIncrementalStates.onCrashOrAnr();
-        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index e6a238a..ba60111 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -814,6 +814,7 @@
         assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs);
         assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs);
         assertArrayEquals(a.resourceDirs, that.resourceDirs);
+        assertArrayEquals(a.overlayPaths, that.overlayPaths);
         assertEquals(a.seInfo, that.seInfo);
         assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles);
         assertEquals(a.dataDir, that.dataDir);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 938e4cc..7297622 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -21,11 +21,14 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
 import android.os.PersistableBundle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -346,4 +349,40 @@
         assertLastPackageUsageSet(
                 testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L);
     }
+
+    @Test
+    public void testOverlayPaths() {
+        final PackageUserState testState = new PackageUserState();
+        assertFalse(testState.setOverlayPaths(null));
+        assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+
+        assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder()
+                .addApkPath("/path/to/some.apk").build()));
+        assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder()
+                .addApkPath("/path/to/some.apk").build()));
+
+        assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+        assertFalse(testState.setOverlayPaths(null));
+    }
+    @Test
+    public void testSharedLibOverlayPaths() {
+        final PackageUserState testState = new PackageUserState();
+        final String LIB_ONE = "lib1";
+        final String LIB_TW0 = "lib2";
+        assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+        assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+                new OverlayPaths.Builder().build()));
+
+        assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+                .addApkPath("/path/to/some.apk").build()));
+        assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+                .addApkPath("/path/to/some.apk").build()));
+        assertTrue(testState.setSharedLibraryOverlayPaths(LIB_TW0, new OverlayPaths.Builder()
+                .addApkPath("/path/to/some.apk").build()));
+
+        assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+                new OverlayPaths.Builder().build()));
+        assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index e6c3d7c..b21b049 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -18,6 +18,7 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertHaveIds;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -25,6 +26,8 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
 
 import android.content.ComponentName;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -47,6 +50,9 @@
  */
 @SmallTest
 public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
+
+    private static final int CACHE_OWNER = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
+
     private List<String> callShellCommand(String... args) throws IOException, RemoteException {
 
         // For reset to work, the current time needs to be incrementing.
@@ -215,11 +221,13 @@
 
     // This command is deprecated. Will remove the test later.
     public void testLauncherCommands() throws Exception {
+        prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ getSystemLauncher().activityInfo.getComponentName(),
                 list(getSystemLauncher(), getFallbackLauncher()),
                 USER_0);
 
+        prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ cn(CALLING_PACKAGE_2, "name"),
                 list(getSystemLauncher(), getFallbackLauncher(),
@@ -241,6 +249,7 @@
                 "Launcher: ComponentInfo{com.android.test.2/name}");
 
         // Change user-0's launcher.
+        prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ cn(CALLING_PACKAGE_1, "name"),
                 list(ri(CALLING_PACKAGE_1, "name", false, 0)),
@@ -323,6 +332,72 @@
         });
     }
 
+    public void testGetShortcuts() throws Exception {
+
+        mRunningUsers.put(USER_10, true);
+
+        // Add two manifests and two dynamics.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_2);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.addDynamicShortcuts(list(
+                    makeLongLivedShortcut("s1"), makeShortcut("s2"))));
+        });
+        runWithCaller(LAUNCHER_1, USER_10, () -> {
+            mInjectCheckAccessShortcutsPermission = true;
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_10,
+                    CACHE_OWNER);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "s1", "s2")
+                    .areAllEnabled()
+
+                    .selectPinned()
+                    .haveIds("ms2", "s2");
+        });
+
+
+        mRunningUsers.put(USER_10, true);
+        mUnlockedUsers.put(USER_10, true);
+
+        mInjectedCallingUid = Process.SHELL_UID;
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+                "s1");
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC), CALLING_PACKAGE_1),
+                "s1", "s2");
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST), CALLING_PACKAGE_1),
+                "ms1", "ms2");
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+                "ms2", "s2");
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC
+                        | ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+                "ms2", "s1", "s2");
+
+        assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+                Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST
+                        | ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+                "ms1", "ms2", "s1");
+
+    }
+
     public void testDumpsysArgs() {
         checkDumpsysArgs(null, true, false, false);
         checkDumpsysArgs(array("-u"), true, true, false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
     @Test
     public void testUserTypeBuilder_createUserType() {
         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+        final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+        final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+        final List<DefaultCrossProfileIntentFilter> filters = List.of(
+                new DefaultCrossProfileIntentFilter.Builder(
+                DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                /* flags= */0,
+                /* letsPersonalDataIntoProfile= */false).build());
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
                 .setEnabled(true)
                 .setMaxAllowed(21)
-                .setBaseType(FLAG_FULL)
+                .setBaseType(FLAG_PROFILE)
                 .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
                 .setBadgeLabels(23, 24, 25)
                 .setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
                 .setLabel(31)
                 .setMaxAllowedPerParent(32)
                 .setDefaultRestrictions(restrictions)
+                .setDefaultSystemSettings(systemSettings)
+                .setDefaultSecureSettings(secureSettings)
+                .setDefaultCrossProfileIntentFilters(filters)
                 .createUserTypeDetails();
 
         assertEquals("a.name", type.getName());
         assertTrue(type.isEnabled());
         assertEquals(21, type.getMaxAllowed());
-        assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+        assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
         assertEquals(28, type.getIconBadge());
         assertEquals(29, type.getBadgePlain());
         assertEquals(30, type.getBadgeNoBackground());
         assertEquals(31, type.getLabel());
         assertEquals(32, type.getMaxAllowedPerParent());
+
         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
         assertNotSame(restrictions, type.getDefaultRestrictions());
 
+        assertNotSame(systemSettings, type.getDefaultSystemSettings());
+        assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+        for (String key : systemSettings.keySet()) {
+            assertEquals(
+                    systemSettings.getString(key),
+                    type.getDefaultSystemSettings().getString(key));
+        }
+
+        assertNotSame(secureSettings, type.getDefaultSecureSettings());
+        assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+        for (String key : secureSettings.keySet()) {
+            assertEquals(
+                    secureSettings.getString(key),
+                    type.getDefaultSecureSettings().getString(key));
+        }
+
+        assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+        assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+        for (int i = 0; i < filters.size(); i++) {
+            assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+        }
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
         assertEquals(Resources.ID_NULL, type.getLabel());
         assertTrue(type.getDefaultRestrictions().isEmpty());
+        assertTrue(type.getDefaultSystemSettings().isEmpty());
+        assertTrue(type.getDefaultSecureSettings().isEmpty());
+        assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
 
         assertFalse(type.hasBadge());
     }
@@ -416,4 +451,13 @@
         }
         return bundle;
     }
+
+    /** Creates a Bundle of the given settings keys and puts true for the value. */
+    private static Bundle makeSettingsBundle(String ... settings) {
+        final Bundle bundle = new Bundle();
+        for (String setting : settings) {
+            bundle.putBoolean(setting, true);
+        }
+        return bundle;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
         assertSame(in, UserRestrictionsUtils.nonNull(in));
     }
 
-    public void testIsEmpty() {
-        assertTrue(UserRestrictionsUtils.isEmpty(null));
-        assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
-        assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
-    }
-
-    public void testClone() {
-        Bundle in = new Bundle();
-        Bundle out = UserRestrictionsUtils.clone(in);
-        assertNotSame(in, out);
-        assertRestrictions(out, new Bundle());
-
-        out = UserRestrictionsUtils.clone(null);
-        assertNotNull(out);
-        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
-    }
-
     public void testMerge() {
         Bundle a = newRestrictions("a", "d");
         Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 61252c4..ff0aec7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -248,6 +248,7 @@
             .ignored("Deferred pre-R, but assigned immediately in R")}
             requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
             resourceDirs=${this.resourceDirs?.contentToString()}
+            overlayPaths=${this.overlayPaths?.contentToString()}
             roundIconRes=${this.roundIconRes}
             scanPublicSourceDir=${this.scanPublicSourceDir
             .ignored("Deferred pre-R, but assigned immediately in R")}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
new file mode 100644
index 0000000..70d85b6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.pm.parsing.library;
+
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidNetIpSecIkeUpdater}
+ */
+@Presubmit
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidNetIpSecIkeUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    @Test
+    public void otherUsesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary("other")
+                .addUsesOptionalLibrary("optional")
+                .addUsesLibrary("android.net.ipsec.ike")
+                .hideAsParsed());
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary("other")
+                .addUsesOptionalLibrary("optional")
+                .hideAsParsed())
+                .hideAsFinal();
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary("android.net.ipsec.ike")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary("android.net.ipsec.ike")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, AndroidNetIpSecIkeUpdater::new);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index 09c8142..9768f17 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -165,6 +165,23 @@
         checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
     }
 
+    /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses a
+     * {@link AndroidNetIpSecIkeUpdater}.
+     */
+    @Test
+    public void android_net_ipsec_ike_in_usesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary("android.net.ipsec.ike")
+                .hideAsParsed());
+
+        ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
+
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+    }
+
     private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
         checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
     }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index d876b05..3ff8e76 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -27,8 +28,10 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorManager;
 import android.os.CombinedVibrationEffect;
 import android.os.IBinder;
 import android.os.PowerManager;
@@ -229,9 +232,9 @@
                 .compose();
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(20);
+        assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
         assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -254,9 +257,9 @@
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(20);
+        assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
         assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -388,6 +391,20 @@
     }
 
     @Test
+    public void vibrate_singleVibrator_skipsSyncedCallbacks() {
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+        long vibrationId = 1;
+        waitForCompletion(startThreadAndDispatcher(vibrationId++,
+                VibrationEffect.createOneShot(10, 100)));
+
+        verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED));
+        verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
+        verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
+        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+    }
+
+    @Test
     public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes()
             throws Exception {
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK);
@@ -484,8 +501,7 @@
     }
 
     @Test
-    public void vibrate_multipleSequential_runsVibrationInOrderWithDelays()
-            throws Exception {
+    public void vibrate_multipleSequential_runsVibrationInOrderWithDelays() throws Exception {
         mockVibrators(1, 2, 3);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -529,6 +545,126 @@
     }
 
     @Test
+    public void vibrate_multipleSyncedCallbackTriggered_finishSteps() throws Exception {
+        int[] vibratorIds = new int[]{1, 2};
+        long vibrationId = 1;
+        mockVibrators(vibratorIds);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
+        when(mThreadCallbacks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+
+        VibrationEffect composed = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+                .compose();
+        CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed);
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+
+        assertTrue(waitUntil(t -> !mVibratorProviders.get(1).getEffects().isEmpty()
+                && !mVibratorProviders.get(2).getEffects().isEmpty(), thread, TEST_TIMEOUT_MILLIS));
+        thread.syncedVibrationComplete();
+        waitForCompletion(thread);
+
+        long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE;
+        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+
+        assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects());
+        assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects());
+    }
+
+    @Test
+    public void vibrate_multipleSynced_callsPrepareAndTriggerCallbacks() {
+        int[] vibratorIds = new int[]{1, 2, 3, 4};
+        mockVibrators(vibratorIds);
+        mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(true);
+
+        long vibrationId = 1;
+        VibrationEffect composed = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .compose();
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+                .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+                .addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1))
+                .addVibrator(4, composed)
+                .combine();
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        waitForCompletion(thread);
+
+        long expectedCap = IVibratorManager.CAP_SYNC
+                | IVibratorManager.CAP_PREPARE_ON
+                | IVibratorManager.CAP_PREPARE_PERFORM
+                | IVibratorManager.CAP_PREPARE_COMPOSE
+                | IVibratorManager.CAP_MIXED_TRIGGER_ON
+                | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM
+                | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+    }
+
+    @Test
+    public void vibrate_multipleSyncedPrepareFailed_skipTriggerStepAndVibrates() {
+        int[] vibratorIds = new int[]{1, 2};
+        mockVibrators(vibratorIds);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
+
+        long vibrationId = 1;
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.createOneShot(10, 100))
+                .addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1))
+                .combine();
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        waitForCompletion(thread);
+
+        long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
+        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mThreadCallbacks, never()).triggerSyncedVibration(eq(vibrationId));
+        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+
+        assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(1).getEffects());
+        assertEquals(Arrays.asList(100), mVibratorProviders.get(1).getAmplitudes());
+        assertEquals(Arrays.asList(expectedOneShot(5)), mVibratorProviders.get(2).getEffects());
+        assertEquals(Arrays.asList(200), mVibratorProviders.get(2).getAmplitudes());
+    }
+
+    @Test
+    public void vibrate_multipleSyncedTriggerFailed_cancelPreparedVibrationAndSkipSetAmplitude() {
+        int[] vibratorIds = new int[]{1, 2};
+        mockVibrators(vibratorIds);
+        mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(false);
+
+        long vibrationId = 1;
+        CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+                .addVibrator(1, VibrationEffect.createOneShot(10, 100))
+                .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+                .combine();
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        waitForCompletion(thread);
+
+        long expectedCap = IVibratorManager.CAP_SYNC
+                | IVibratorManager.CAP_PREPARE_ON
+                | IVibratorManager.CAP_PREPARE_PERFORM
+                | IVibratorManager.CAP_MIXED_TRIGGER_ON
+                | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+        verify(mThreadCallbacks).cancelSyncedVibration();
+        assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
+    }
+
+    @Test
     public void vibrate_multipleWaveforms_playsWaveformsInParallel() throws Exception {
         mockVibrators(1, 2, 3);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -621,10 +757,9 @@
                 .combine();
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(10);
+        assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
         assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
-        assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
new file mode 100644
index 0000000..f5d0ca7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.wm;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.rotationresolver.RotationResolverInternal;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.wm.WindowOrientationListener}
+ */
+public class WindowOrientationListenerTest {
+
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private Handler mMockHandler;
+    @Mock
+    private InputSensorInfo mMockInputSensorInfo;
+    @Mock
+    private SensorManager mMockSensorManager;
+    @Mock
+    private WindowManagerService mMockWindowManagerService;
+
+    private TestableRotationResolver mFakeRotationResolverInternal;
+    private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
+    private int mFinalizedRotation;
+    private boolean mRotationResolverEnabled;
+    private boolean mCanUseRotationResolver;
+    private SensorEvent mFakeSensorEvent;
+    private Sensor mFakeSensor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mRotationResolverEnabled = true;
+        mCanUseRotationResolver = true;
+
+        mFakeRotationResolverInternal = new TestableRotationResolver();
+        doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
+        mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+                mMockHandler, mMockWindowManagerService);
+        mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+
+        mFakeSensor = new Sensor(mMockInputSensorInfo);
+        mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L,
+                new float[]{(float) Surface.ROTATION_90});
+    }
+
+    @Test
+    public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() {
+        mRotationResolverEnabled = false;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+    }
+
+    @Test
+    public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
+        mCanUseRotationResolver = false;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+
+    }
+
+    @Test
+    public void testOnSensorChanged_normalCase() {
+        mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+    }
+
+    final class TestableRotationResolver extends RotationResolverInternal {
+        @Surface.Rotation
+        int mResult;
+
+        @Override
+        public boolean isRotationResolverSupported() {
+            return true;
+        }
+
+        @Override
+        public void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
+                @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
+                @DurationMillisLong long timeoutMillis,
+                @NonNull CancellationSignal cancellationSignal) {
+            callback.onSuccess(mResult);
+        }
+    }
+
+    final class TestableWindowOrientationListener extends WindowOrientationListener {
+
+        TestableWindowOrientationListener(Context context, Handler handler,
+                WindowManagerService service) {
+            super(context, handler, service);
+            this.mOrientationJudge = new OrientationSensorJudge();
+        }
+
+        @Override
+        public void onProposedRotationChanged(int rotation) {
+            mFinalizedRotation = rotation;
+        }
+
+        @Override
+        public boolean canUseRotationResolver() {
+            return mCanUseRotationResolver;
+        }
+
+        @Override
+        public boolean isRotationResolverEnabled() {
+            return mRotationResolverEnabled;
+        }
+    }
+}
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
index c2cb6881..35ca354 100644
--- a/services/tests/shortcutmanagerutils/Android.bp
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -22,5 +22,9 @@
         "android.test.runner.stubs",
     ],
 
+    static_libs: [
+        "compatibility-device-util-axt",
+    ],
+
     sdk_version: "test_current",
 }
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 907f887..5182b3b 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.pm.shortcutmanagertest;
 
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -34,6 +36,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Instrumentation;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.LocusId;
@@ -50,6 +53,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.test.MoreAsserts;
 import android.util.Log;
 
@@ -136,6 +140,20 @@
         return sb.toString();
     }
 
+    public static List<String> extractShortcutIds(List<String> result) {
+        final String prefix = "ShortcutInfo {id=";
+        final String postfix = ", ";
+
+        List<String> ids = new ArrayList<>();
+        for (String line : result) {
+            if (line.contains(prefix)) {
+                ids.add(line.substring(
+                        line.indexOf(prefix) + prefix.length(), line.indexOf(postfix)));
+            }
+        }
+        return ids;
+    }
+
     public static boolean resultContains(List<String> result, String expected) {
         for (String line : result) {
             if (line.contains(expected)) {
@@ -160,6 +178,16 @@
         return result;
     }
 
+    public static List<String> assertHaveIds(List<String> result, String... expectedIds) {
+        assertSuccess(result);
+
+        final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
+        final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result));
+        assertEquals(expected, actual);
+
+        return result;
+    }
+
     public static List<String> runCommand(Instrumentation instrumentation, String command) {
         return runCommand(instrumentation, command, null);
     }
@@ -193,30 +221,60 @@
         return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
     }
 
-    public static String getDefaultLauncher(Instrumentation instrumentation) {
-        final String PREFIX = "Launcher: ComponentInfo{";
-        final String POSTFIX = "}";
-        final List<String> result = runShortcutCommandForSuccess(
-                instrumentation, "get-default-launcher --user "
-                        + instrumentation.getContext().getUserId());
-        for (String s : result) {
-            if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
-                return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+    private static UserHandle getParentUser(Context context) {
+        final UserHandle user = context.getUser();
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        if (!userManager.isManagedProfile(user.getIdentifier())) {
+            return user;
+        }
+
+        final List<UserHandle> profiles = userManager.getUserProfiles();
+        for (UserHandle handle : profiles) {
+            if (!userManager.isManagedProfile(handle.getIdentifier())) {
+                return handle;
             }
         }
-        fail("Default launcher not found");
         return null;
     }
 
-    public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
-        runCommand(instrumentation, "cmd package set-home-activity --user "
-                + instrumentation.getContext().getUserId() + " " + component,
-                result -> result.contains("Success"));
+    public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
+        final Context context = instrumentation.getContext();
+        final RoleManager roleManager = context.getSystemService(RoleManager.class);
+        final UserHandle user = getParentUser(context);
+        List<String> roleHolders = callWithShellPermissionIdentity(
+                () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
+        if (roleHolders.size() == 1) {
+            return roleHolders.get(0);
+        }
+        fail("Failed to get the default launcher for user " + context.getUserId());
+        return null;
+    }
+
+    public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
+        runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
+                + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+                + packageName + " 0");
+        waitUntil("Failed to get shortcut access",
+                () -> hasShortcutAccess(instrumentation, packageName), 20);
     }
 
     public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
-        setDefaultLauncher(instrumentation, packageContext.getPackageName()
-                + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+        setDefaultLauncher(instrumentation, packageContext.getPackageName());
+    }
+
+    public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
+        final List<String> result = runShortcutCommandForSuccess(instrumentation,
+                "has-shortcut-access --user " + instrumentation.getContext().getUserId()
+                        + " " + packageName);
+        for (String s : result) {
+            if (s.startsWith("true")) {
+                return true;
+            } else if (s.startsWith("false")) {
+                return false;
+            }
+        }
+        fail("Failed to check shortcut access");
+        return false;
     }
 
     public static void overrideConfig(Instrumentation instrumentation, String config) {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index e5646db..1dd4212 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -60,6 +60,6 @@
         "libui",
         "libunwindstack",
         "libutils",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-V5-cpp",
     ],
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index b3116d9..17c9411 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.notification;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.any;
@@ -138,6 +141,30 @@
     }
 
     @Test
+    public void testXmlMigratingAllowedAdjustments() throws Exception {
+        // Old tag, need migration
+        String xml = "<q_allowed_adjustments types=\"adj_1\"/>";
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes())), null);
+        parser.nextTag();
+        mAssistants.readExtraTag("q_allowed_adjustments", parser);
+        assertTrue(mAssistants.isAdjustmentAllowed("adj_1"));
+        assertEquals(mNm.DEFAULT_ALLOWED_ADJUSTMENTS.length + 1,
+                mAssistants.getAllowedAssistantAdjustments().size());
+
+        // New TAG
+        xml = "<s_allowed_adjustments types=\"adj_2\"/>";
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes())), null);
+        parser.nextTag();
+        mAssistants.readExtraTag("s_allowed_adjustments", parser);
+        assertTrue(mAssistants.isAdjustmentAllowed("adj_2"));
+        assertEquals(1, mAssistants.getAllowedAssistantAdjustments().size());
+    }
+
+    @Test
     public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
         ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
         ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cf977b4..ddf284401 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -51,7 +51,7 @@
     ],
 
     libs: [
-        "android.hardware.power-java",
+        "android.hardware.power-V1-java",
         "android.test.mock",
         "android.test.base",
         "android.test.runner",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 6b69ee9..002859e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -96,6 +96,8 @@
                 .setTask(mTrampolineActivity.getTask())
                 .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
                 .build();
+        // becomes invisible when covered by mTopActivity
+        mTrampolineActivity.mVisibleRequested = false;
     }
 
     @After
@@ -230,7 +232,6 @@
 
     @Test
     public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
-        mTopActivity.mVisibleRequested = true;
         doReturn(true).when(mTopActivity).isReportedDrawn();
 
         // Create an activity with different process that meets process switch.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 42b080e..72b8439 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
                 new RemoteAnimationAdapter(new Stub() {
 
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                     }
 
@@ -2178,6 +2180,7 @@
 
         activity.setOccludesParent(true);
         activity.setVisible(false);
+        activity.mVisibleRequested = false;
         // Can not specify orientation if app isn't visible even though it occludes parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 22ee886..3a7954b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1424,6 +1424,9 @@
         final ActivityRecord nonTopVisibleActivity =
                 new ActivityBuilder(mAtm).setTask(task).build();
         new ActivityBuilder(mAtm).setTask(task).build();
+        // The scenario we are testing is when the app isn't visible yet.
+        nonTopVisibleActivity.setVisible(false);
+        nonTopVisibleActivity.mVisibleRequested = false;
         doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
         doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
         doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 36adf28..37bc23e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -844,6 +844,9 @@
         final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
                 secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                         ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        // Activity should start invisible since we are bringing it to front.
+        singleTaskActivity.setVisible(false);
+        singleTaskActivity.mVisibleRequested = false;
 
         // Create another activity on top of the secondary display.
         final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -70,8 +71,10 @@
 
     class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) {
             for (RemoteAnimationTarget target : apps) {
                 assertNotNull(target.startBounds);
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 f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
     private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         boolean mCancelled = false;
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 2f4c8e2..d13e4dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -15,7 +15,7 @@
  */
 
 package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
+
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -587,7 +587,7 @@
 
         final WindowToken token = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, null /* options */);
         policy.addWindow(token);
 
@@ -621,11 +621,11 @@
 
         final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, null /* options */);
         final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, null /* options */);
         policy.addWindow(token1);
         policy.addWindow(token2);
@@ -672,15 +672,15 @@
         options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
         final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, null /* options */);
         final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, options1);
         final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
                 false /* fromClientToken */, options2);
 
         policy.addWindow(token0);
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 4bea9a2..b4fd302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1379,7 +1379,7 @@
     }
 
     @Test
-    public void testNoFixedRotationWithPip() {
+    public void testFixedRotationWithPip() {
         final DisplayContent displayContent = mDefaultDisplay;
         unblockDisplayRotation(displayContent);
         // Make resume-top really update the activity state.
@@ -1406,15 +1406,20 @@
         assertEquals(homeConfigOrientation, displayConfig.orientation);
 
         clearInvocations(mWm);
-        // Leave PiP to fullscreen. The orientation can be updated from
-        // ActivityRecord#reportDescendantOrientationChangeIfNeeded.
-        pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
+        // to fullscreen, so fixed rotation will apply on it.
+        pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         homeActivity.setState(Task.ActivityState.STOPPED, "test");
 
-        assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
-        verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
-        assertEquals(pinnedConfigOrientation, displayConfig.orientation);
+        assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
+        verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
+        assertNotEquals(pinnedConfigOrientation, displayConfig.orientation);
+
+        // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
+        pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
         assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+        assertEquals(pinnedConfigOrientation, displayConfig.orientation);
 
         clearInvocations(mWm);
         // Enter PiP from fullscreen. The orientation can be updated from
@@ -1608,6 +1613,16 @@
     }
 
     @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);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 33e8fc0..3e05c86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -217,6 +217,7 @@
         SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
         overrideSettings.mShouldShowSystemDecors = true;
         overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+        overrideSettings.mDontMoveToTop = true;
         provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
         assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
 
@@ -227,6 +228,8 @@
                 getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors"));
         assertEquals("Attribute value must be stored", "0",
                 getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy"));
+        assertEquals("Attribute value must be stored", "true",
+                getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop"));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
         mColor = Color.GREEN;
 
         assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+        mLetterbox.applySurfaceChanges(mTransaction);
+
+        verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         adapter.onAnimationCancelled(mMockLeash);
         verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         mClock.fastForward(2500);
         mHandler.timeAdvance();
@@ -170,7 +176,7 @@
                     null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
             mClock.fastForward(2500);
             mHandler.timeAdvance();
@@ -190,7 +196,7 @@
 
     @Test
     public void testZeroAnimations() {
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_NONE);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -199,7 +205,7 @@
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -213,15 +219,18 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                 ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-        verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+        verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                 finishedCaptor.capture());
         assertEquals(1, appsCaptor.getValue().length);
         assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
         verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
                 eq(adapter));
@@ -255,15 +264,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
         } finally {
@@ -383,15 +401,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 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.assertNotEquals;
@@ -119,13 +117,13 @@
     @Test
     public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
         removeGlobalMinSizeRestriction();
-        // Create landscape freeform display and a freeform app.
+        // create freeform display and a freeform app
         DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
                 .setCanRotate(false)
                 .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
         setUpApp(display);
 
-        // Put app window into portrait freeform and then make it a compat app.
+        // Put app window into freeform and then make it a compat app.
         final Rect bounds = new Rect(100, 100, 400, 600);
         mTask.setBounds(bounds);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
 
         final int density = mActivity.getConfiguration().densityDpi;
 
-        // Change display configuration to fullscreen.
+        // change display configuration to fullscreen
         Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
         c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
         assertEquals(bounds.width(), mActivity.getBounds().width());
         assertEquals(bounds.height(), mActivity.getBounds().height());
         assertEquals(density, mActivity.getConfiguration().densityDpi);
-        // Size compat mode is sandboxed at the activity level.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -175,12 +171,6 @@
         assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
         // The decor height should be a part of the effective bounds.
         assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertFitted();
@@ -190,17 +180,9 @@
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
         // The notch is no longer on top.
         assertEquals(appBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
     }
 
     @Test
@@ -228,9 +210,6 @@
         assertEquals(originalBounds.width(), mActivity.getBounds().width());
         assertEquals(originalBounds.height(), mActivity.getBounds().height());
         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
-        // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
-        // max aspect ratio.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
         assertScaled();
     }
 
@@ -238,13 +217,11 @@
     public void testFixedScreenBoundsWhenDisplaySizeChanged() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        final DisplayContent display = mActivity.mDisplayContent;
         assertFitted();
-        // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         final Rect origBounds = new Rect(mActivity.getBounds());
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+        final DisplayContent display = mActivity.mDisplayContent;
 
         // Change the size of current display.
         resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
         // The position of configuration bounds should be the same as compat bounds.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display size to a different orientation
         resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // The previous resize operation doesn't consider the rotation change after size changed.
         // These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(offsetX, currentBounds.left);
         assertScaled();
-        // Activity is sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -309,8 +280,6 @@
         assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
         // The position should be horizontal centered.
         assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
         // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
         // It should keep non-attachable because the resolved bounds will be computed according to
         // the aspect ratio that won't match its parent bounds.
         assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -349,13 +316,14 @@
     }
 
     @Test
-    public void testMoveToDifferentOrientationDisplay() {
+    public void testMoveToDifferentOrientDisplay() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
-        final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
-        final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+        final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+        final int origWidth = configBounds.width();
+        final int origHeight = configBounds.height();
 
         final int notchHeight = 100;
         final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
         // Move the non-resizable activity to the new display.
         mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
         // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
-        assertEquals(originalBounds.width(), currentBounds.width());
-        assertEquals(originalBounds.height(), currentBounds.height());
+        assertEquals(origWidth, configBounds.width());
+        assertEquals(origHeight, configBounds.height());
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode on the new display.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
         // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
         assertEquals(newDisplayBounds.height() - notchHeight,
-                (int) ((float) mActivity.getBounds().width() * originalBounds.height()
-                        / originalBounds.width()));
+                (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
 
         // Recompute the natural configuration in the new display.
         mActivity.clearSizeCompatMode();
         mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
-        assertEquals(newDisplayBounds.height(), currentBounds.height());
-        assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
-                currentBounds.width());
+        assertEquals(newDisplayBounds.height(), configBounds.height());
+        assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+                configBounds.width());
         assertFitted();
         // The appBounds should be [200, 100 - 700, 1000].
         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
-        assertEquals(currentBounds.width(), appBounds.width());
-        assertEquals(currentBounds.height() - notchHeight, appBounds.height());
-        // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(configBounds.width(), appBounds.width());
+        assertEquals(configBounds.height() - notchHeight, appBounds.height());
     }
 
     @Test
-    public void testFixedOrientationRotateCutoutDisplay() {
+    public void testFixedOrientRotateCutoutDisplay() {
         // Create a display with a notch/cutout
         final int notchHeight = 60;
-        final int width = 1000;
-        setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+        setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
                 .setNotch(notchHeight).build());
-        // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
-        final float maxAspect = 1.4f;
+        // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
         prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
         final Rect origBounds = new Rect(currentBounds);
         final Rect origAppBounds = new Rect(appBounds);
 
-        // Activity is sandboxed, and bounds include the area consumed by the notch.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
         // Although the activity is fixed orientation, force rotate the display.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
         assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
         // The position in configuration should be global coordinates.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
-    public void testFixedAspectRatioOrientationChangeOrientation() {
+    public void testFixedAspOrientChangeOrient() {
         setUpDisplaySizeWithApp(1000, 2500);
 
         final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
         final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
 
         assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         // Change the fixed orientation.
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
                 mActivity.getWindowConfiguration().getAppBounds().height());
         assertEquals(originalAppBounds.height(),
                 mActivity.getWindowConfiguration().getAppBounds().width());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -510,8 +459,6 @@
         // restarted and the override configuration won't be cleared.
         verify(mActivity, never()).restartProcessIfVisible();
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display density
         display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
         // in multi-window mode.
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         // The non-resizable activity should not be size compat because the display support
         // changing windowing mode from fullscreen to freeform.
         mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
     }
 
     @Test
@@ -659,9 +602,6 @@
         // be transparent.
         assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
 
-        // Activity is sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-
         // Make the activity fill the display.
         prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
         // The letterbox should only cover the notch area, so status bar can be transparent.
         assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
         assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -696,8 +635,6 @@
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(taskBounds, activityBounds);
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertTaskMaxBoundsSandboxed();
 
         // Task bounds should be 700x1400 with the ratio as the display.
         assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
         assertScaled();
         assertEquals(activityBounds.width(), newActivityBounds.width());
         assertEquals(activityBounds.height(), newActivityBounds.height());
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -741,30 +676,29 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
+        Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        Rect activityBounds = new Rect(mActivity.getBounds());
+
         // App should launch in fullscreen.
         assertFalse(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Activity and task inherit max bounds from TaskDisplayArea.
-        assertMaxBoundsInheritDisplayAreaBounds();
+        assertEquals(displayBounds, activityBounds);
 
         // Rotate display to landscape.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
-        final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
-        final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
-        assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+        displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        activityBounds = new Rect(mActivity.getBounds());
+        assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // App bounds should be 700x1400 with the ratio as the display.
-        assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
-        assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
-                        / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                activityBounds.width());
     }
 
     @Test
@@ -797,17 +731,14 @@
         final Rect displayBounds = new Rect(display.getBounds());
         final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect newActivityBounds = new Rect(newActivity.getBounds());
-        final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
 
         // Task and app bounds should be 700x1400 with the ratio as the display.
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(newActivity.inSizeCompatMode());
         assertEquals(taskBounds, newActivityBounds);
         assertEquals(displayBounds.height(), taskBounds.height());
-        assertThat(taskBounds.width())
-                .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                taskBounds.width());
     }
 
     @Test
@@ -847,14 +778,6 @@
         assertEquals(displayBounds.height(), taskBounds.height());
         assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
                 taskBounds.width());
-        // New activity max bounds are sandboxed due to letterbox.
-        assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskBounds);
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
-                .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
 
         // App bounds should be fullscreen in Task bounds.
         assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
         assertEquals(activityBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed due to size compat.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -913,7 +831,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
 
         // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -947,26 +862,20 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed due to mismatched orientation request.
-        assertTaskMaxBoundsSandboxed();
 
-        // Rotate display to landscape.
+        // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        // Activity max bounds are sandboxed due to unresizable app.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
-        // Rotate display to portrait.
+        // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
 
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed, as in first case.
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -983,18 +892,12 @@
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(2800, displayBounds.width());
         assertEquals(1400, displayBounds.height());
-        Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
-        taskDisplayArea.setBounds(displayAreaBounds);
+        taskDisplayArea.setBounds(0, 0, 2400, 1000);
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(2400, activityBounds.width());
         assertEquals(1000, activityBounds.height());
-        // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
     }
 
     @Test
@@ -1142,48 +1045,6 @@
         assertFalse(mActivity.hasSizeCompatBounds());
     }
 
-    /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
-    private void assertMaxBoundsInheritDisplayAreaBounds() {
-        final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-    }
-
-    /**
-     * Asserts task-level letterboxing, so both activity and task max bounds
-     * are sandboxed to the letterbox bounds.
-     */
-    private void assertTaskMaxBoundsSandboxed() {
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-        // Task max bounds are sandboxed due to letterbox.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-    }
-
-    /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForSizeCompat() {
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getWindowConfiguration().getBounds());
-        // Task inherits max bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
-    /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForLetterbox() {
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getBounds());
-        // Task inherits bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
     static Configuration rotateDisplay(DisplayContent display, int rotation) {
         final Configuration c = new Configuration();
         display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b8d44f6..6c72249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -152,12 +152,6 @@
     }
 
     @Override
-    public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
-            SurfaceControl newParent) {
-        return this;
-    }
-
-    @Override
     public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
         return this;
     }
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 d83e9c2..e22cda6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -156,6 +156,9 @@
         spyOn(mDisplayContent);
         doReturn(true).when(mDisplayContent).isTrusted();
 
+        // Allow child stack 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 */);
@@ -173,6 +176,65 @@
     }
 
     @Test
+    public void testMovingChildTaskOnTop() {
+        // Make sure the display is trusted display which capable to move the stack to top.
+        spyOn(mDisplayContent);
+        doReturn(true).when(mDisplayContent).isTrusted();
+
+        // Allow child stack 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 */);
+
+        // Add another display at top.
+        mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+                false /* includingParents */);
+
+        // Ensure that original display ({@code mDisplayContent}) is not on top.
+        assertEquals("Testing DisplayContent should not be on the top",
+                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 */);
+
+        // Ensure that original display ({@code mDisplayContent}) is now on the top.
+        assertEquals("The testing DisplayContent should be moved to top with task",
+                mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+    }
+
+    @Test
+    public void testDontMovingChildTaskOnTop() {
+        // Make sure the display is trusted display which capable to move the stack to top.
+        spyOn(mDisplayContent);
+        doReturn(true).when(mDisplayContent).isTrusted();
+
+        // Allow child stack 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 */);
+
+        // Add another display at top.
+        mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+                false /* includingParents */);
+
+        // Ensure that original display ({@code mDisplayContent}) is not on top.
+        assertEquals("Testing DisplayContent should not be on the top",
+                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 */);
+
+        // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
+        // on the top.
+        assertEquals("The testing DisplayContent should not be moved to top with task",
+                mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+    }
+
+    @Test
     public void testReuseTaskAsRootTask() {
         final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, mDisplayContent);
@@ -233,7 +295,7 @@
         homeActivity.mVisibleRequested = true;
         assertFalse(rootHomeTask.isVisible());
 
-        assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
+        assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
     }
 
     @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();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
             final TestDisplayContent newDisplay = createInternal(display);
-            // Ensure letterbox aspect ratio is not overridden on any device target.
-            // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
-            // the below method, is set on some device form factors.
-            mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
             // disable the normal system decorations
             final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
             spyOn(displayPolicy);
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 df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -905,8 +906,10 @@
         final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                 new IRemoteAnimationRunner.Stub() {
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                         try {
                             finishedCallback.onAnimationFinished();
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 f8a89c6..6d0e510 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -20,7 +20,6 @@
 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.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -37,12 +36,10 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.pm.PackageManager;
 import android.os.IBinder;
@@ -75,7 +72,7 @@
     @Test
     public void testAddWindowToken() {
         IBinder token = mock(IBinder.class);
-        mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId());
+        mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */);
 
         WindowToken windowToken = mWm.mRoot.getWindowToken(token);
         assertFalse(windowToken.mRoundedCornerOverlay);
@@ -83,32 +80,6 @@
     }
 
     @Test
-    public void testAddWindowTokenWithOptions() {
-        IBinder token = mock(IBinder.class);
-        mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
-                null /* options */, null /* options */);
-
-        WindowToken windowToken = mWm.mRoot.getWindowToken(token);
-        assertFalse(windowToken.mRoundedCornerOverlay);
-        assertTrue(windowToken.isFromClient());
-    }
-
-    @Test(expected = SecurityException.class)
-    public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
-        IBinder token = mock(IBinder.class);
-        mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
-                null /* options */, null /* options */);
-
-        spyOn(mWm);
-        when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
-        WindowToken windowToken = mWm.mRoot.getWindowToken(token);
-        spyOn(windowToken);
-        when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
-
-        mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
-    }
-
-    @Test
     public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
         DisplayContent display = createNewDisplay();
         // Current focused window
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 3492d90..eb6c6ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -890,6 +890,7 @@
                     mTask.moveToFront("createActivity");
                 }
                 // Make visible by default...
+                activity.mVisibleRequested = true;
                 activity.setVisible(true);
             }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 2d27331..e2585e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.os.Process.INVALID_UID;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -201,7 +200,7 @@
 
         token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
                 true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
-                INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
+                true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
         assertTrue(token.mRoundedCornerOverlay);
         assertTrue(token.isFromClient());
     }
@@ -215,8 +214,8 @@
     public void testSurfaceCreatedForWindowToken() {
         final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
                 mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
-                mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
-                true /* roundedCornerOverlay */, true /* fromClientToken */);
+                mDisplayContent, true /* ownerCanManageAppTokens */,
+                true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
         assertNull(fromClientToken.mSurfaceControl);
 
         createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window");
@@ -224,8 +223,8 @@
 
         final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
                 mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
-                false /* fromClientToken */);
+                true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+                false /* fromClientToken */, null /* options */);
         assertNotNull(nonClientToken.mSurfaceControl);
     }
 
@@ -238,7 +237,7 @@
 
         final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
                 false /* fromClientToken */, null /* options */);
 
         verify(selectFunc).apply(token1.windowType, null);
@@ -246,7 +245,7 @@
         final Bundle options = new Bundle();
         final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
                 TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
-                true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+                true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
                 false /* fromClientToken */, options /* options */);
 
         verify(selectFunc).apply(token2.windowType, options);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0cb1255..9e419d4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1526,15 +1526,19 @@
 
         @Override
         public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
-                long endTime, String callingPackage) {
+                long endTime, String callingPackage, int userId) {
             if (!hasPermission(callingPackage)) {
                 return null;
             }
 
-            final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
-                    Binder.getCallingUid(), UserHandle.getCallingUserId());
+            final int callingUid = Binder.getCallingUid();
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+                    userId, false, true, "queryUsageStats", callingPackage);
 
-            final int userId = UserHandle.getCallingUserId();
+            // Check the caller's userId for obfuscation decision, not the user being queried
+            final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
+                    callingUid, UserHandle.getCallingUserId());
+
             final long token = Binder.clearCallingIdentity();
             try {
                 final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index be36f82..f687e4b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -581,17 +581,24 @@
             }
         }
 
-        VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) {
-            List<ResolveInfo> available =
-                    mContext.getPackageManager().queryIntentServicesAsUser(
-                            new Intent(VoiceInteractionService.SERVICE_INTERFACE)
-                                    .setPackage(packageName),
-                            PackageManager.GET_META_DATA
-                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+        private List<ResolveInfo> queryInteractorServices(
+                @UserIdInt int user,
+                @Nullable String packageName) {
+            return mContext.getPackageManager().queryIntentServicesAsUser(
+                    new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(packageName),
+                    PackageManager.GET_META_DATA
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                    user);
+        }
+
+        VoiceInteractionServiceInfo findAvailInteractor(
+                @UserIdInt int user,
+                @Nullable String packageName) {
+            List<ResolveInfo> available = queryInteractorServices(user, packageName);
             int numAvailable = available.size();
             if (numAvailable == 0) {
-                Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
+                Slog.w(TAG, "no available voice interaction services found for user " + user);
                 return null;
             }
             // Find first system package.  We never want to allow third party services to
@@ -1643,13 +1650,7 @@
                     String pkg = roleHolders.get(0);
 
                     // Try to set role holder as VoiceInteractionService
-                    List<ResolveInfo> services = mPm.queryIntentServicesAsUser(
-                            new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg),
-                            PackageManager.GET_META_DATA
-                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
-
-                    for (ResolveInfo resolveInfo : services) {
+                    for (ResolveInfo resolveInfo : queryInteractorServices(userId, pkg)) {
                         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
 
                         VoiceInteractionServiceInfo voiceInteractionServiceInfo =
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9e8f8eaa..04dea3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -163,8 +163,13 @@
         mValid = true;
         mSessionComponentName = new ComponentName(service.getPackageName(),
                 mInfo.getSessionService());
-        // TODO : Need to get the hotword detection service from the xml metadata
-        mHotwordDetectionComponentName = null;
+        final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
+        if (hotwordDetectionServiceName != null) {
+            mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
+                    hotwordDetectionServiceName);
+        } else {
+            mHotwordDetectionComponentName = null;
+        }
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         IntentFilter filter = new IntentFilter();
@@ -388,7 +393,11 @@
         if (DEBUG) {
             Slog.d(TAG, "setHotwordDetectionConfigLocked");
         }
-
+        if (mHotwordDetectionComponentName == null) {
+            Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
+                    + " name not found");
+            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+        }
         if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
             return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index cfdc568..84f4f6a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -165,7 +165,8 @@
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
         if (mBound) {
             try {
-                mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY);
+                mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY,
+                        null /* options */);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed adding window token", e);
             }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ac584c1..21cf3e5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4748,6 +4748,21 @@
     public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
             "network_temp_not_metered_supported_bool";
 
+    /*
+     * Boolean indicating whether the SIM PIN can be stored and verified
+     * seamlessly after an unattended reboot.
+     *
+     * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
+     * ultimately controls whether this carrier configuration option is used.  Where
+     * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the
+     * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is
+     * ignored.
+     *
+     * @hide
+     */
+    public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
+            "store_sim_pin_for_unattended_reboot_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -5304,6 +5319,7 @@
         sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
         sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
         sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
+        sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
     }
 
     /**
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 0059ad6..ae7d209 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -402,29 +402,27 @@
          * @see #getThresholds() for more details on signal strength thresholds
          */
         public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
-            Objects.requireNonNull(thresholds, "thresholds must not be null");
-            if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
-                throw new IllegalArgumentException(
-                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
-            }
-            mThresholds = thresholds.clone();
-            Arrays.sort(mThresholds);
-            return this;
+            return setThresholds(thresholds, false /*isSystem*/);
         }
 
         /**
-         * Set the signal strength thresholds for the corresponding signal measurement type without
-         * the length limitation.
+         * Set the signal strength thresholds for the corresponding signal measurement type.
          *
          * @param thresholds array of integer as the signal threshold values
+         * @param isSystem true is the caller is system which does not have restrictions on
+         *        the length of thresholds array.
          * @return the builder to facilitate the chaining
          *
          * @hide
          */
-        public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+        public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
             Objects.requireNonNull(thresholds, "thresholds must not be null");
+            if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+                throw new IllegalArgumentException(
+                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+            }
             mThresholds = thresholds.clone();
             Arrays.sort(mThresholds);
             return this;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
     public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
             "cache_key.telephony.get_slot_index";
 
+    /** @hide */
+    public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+    /** @hide */
+    public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+            "restoreSimSpecificSettings";
+
+    /**
+     * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+     * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+     * #restoreAllSimSpecificSettings()}.
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
     private static final int MAX_CACHE_SIZE = 4;
 
     private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
     public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "wfc_roaming_enabled");
 
+
+    /**
+     * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+     * settings
+     * <p>
+     * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+     * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "backup_and_restore");
+
+    /**
+     * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+     * SuW.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
     /**
      * A content {@link Uri} used to receive updates on cross sim enabled user setting.
      * <p>
@@ -3455,4 +3494,71 @@
         sSlotIndexCache.clear();
         sPhoneIdCache.clear();
     }
+
+    /**
+     * Called to retrieve SIM-specific settings data to be backed up.
+     *
+     * @return data in byte[] to be backed up.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public byte[] getAllSimSpecificSettingsForBackup() {
+        Bundle bundle =  mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+        return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+    }
+
+    /**
+     * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+     * This will try to restore the data that was stored internally when {@link
+     * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+     * End result is SimInfoDB is modified to match any backed up configs for the requested
+     * inserted sim.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param iccId of the sim that a restore is requested for.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                iccId, null);
+    }
+
+    /**
+     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+     * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+     * data in an internal file. This file will persist on device for device's lifetime and will be
+     * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+     * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+     * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param data with the sim specific configs to be backed up.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+        Bundle bundle = new Bundle();
+        bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                null, bundle);
+    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65b8de2..16ffd9e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11255,16 +11255,21 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
-     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     * Additionally, depending on the level of location permissions the caller holds (i.e. no
+     * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will
+     * be cleared from the return value.
+     *
+     * <p>Note also that if the caller holds any sort of location permission, a usage event for the
+     * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or
+     * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION}
+     * will be logged against the caller when calling this method.
      *
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(allOf = {
-            Manifest.permission.READ_PHONE_STATE,
-            Manifest.permission.ACCESS_COARSE_LOCATION
-    })
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     public @Nullable ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
@@ -14994,6 +14999,7 @@
         }
         return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
     }
+
     /**
      * Registers a listener object to receive notification of changes
      * in specified telephony states.
@@ -15350,4 +15356,66 @@
             return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
         }
     }
+
+    /**
+     * The unattended reboot was prepared successfully.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0;
+
+    /**
+     * The unattended reboot was prepared, but the user will need to manually
+     * enter the PIN code of at least one SIM card present in the device.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
+
+    /**
+     * The unattended reboot was not prepared due to generic error.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"},
+            value = {
+                    PREPARE_UNATTENDED_REBOOT_SUCCESS,
+                    PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED,
+                    PREPARE_UNATTENDED_REBOOT_ERROR
+            })
+    public @interface PrepareUnattendedRebootResult {}
+
+    /**
+     * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done
+     * shortly (e.g. within 15 seconds) after the API is invoked.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#REBOOT}
+     *
+     * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+     * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+     * at least one SIM card for which the user needs to manually enter the PIN
+     * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+     * of error.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REBOOT)
+    @PrepareUnattendedRebootResult
+    public int prepareForUnattendedReboot() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.prepareForUnattendedReboot();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e);
+            e.rethrowFromSystemServer();
+        }
+        return PREPARE_UNATTENDED_REBOOT_ERROR;
+    }
 }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index f48ddb0..bd4bf07 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,7 +136,7 @@
     private final @HandoverFailureMode int mHandoverFailureMode;
     private final int mPduSessionId;
     private final Qos mDefaultQos;
-    private final List<QosSession> mQosSessions;
+    private final List<QosBearerSession> mQosBearerSessions;
     private final SliceInfo mSliceInfo;
 
     /**
@@ -187,7 +187,7 @@
         mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
         mPduSessionId = PDU_SESSION_ID_NOT_SET;
         mDefaultQos = null;
-        mQosSessions = new ArrayList<>();
+        mQosBearerSessions = new ArrayList<>();
         mSliceInfo = null;
     }
 
@@ -197,7 +197,7 @@
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
             @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
             @HandoverFailureMode int handoverFailureMode, int pduSessionId,
-            @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+            @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
             @Nullable SliceInfo sliceInfo) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
@@ -219,7 +219,7 @@
         mHandoverFailureMode = handoverFailureMode;
         mPduSessionId = pduSessionId;
         mDefaultQos = defaultQos;
-        mQosSessions = qosSessions;
+        mQosBearerSessions = qosBearerSessions;
         mSliceInfo = sliceInfo;
     }
 
@@ -246,8 +246,8 @@
         mHandoverFailureMode = source.readInt();
         mPduSessionId = source.readInt();
         mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
-        mQosSessions = new ArrayList<>();
-        source.readList(mQosSessions, QosSession.class.getClassLoader());
+        mQosBearerSessions = new ArrayList<>();
+        source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
         mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
     }
 
@@ -394,8 +394,8 @@
      * @hide
      */
     @NonNull
-    public List<QosSession> getQosSessions() {
-        return mQosSessions;
+    public List<QosBearerSession> getQosBearerSessions() {
+        return mQosBearerSessions;
     }
 
     /**
@@ -427,7 +427,7 @@
            .append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode))
            .append(" pduSessionId=").append(getPduSessionId())
            .append(" defaultQos=").append(mDefaultQos)
-           .append(" qosSessions=").append(mQosSessions)
+           .append(" qosBearerSessions=").append(mQosBearerSessions)
            .append(" sliceInfo=").append(mSliceInfo)
            .append("}");
         return sb.toString();
@@ -447,10 +447,10 @@
                 mDefaultQos == other.mDefaultQos :
                 mDefaultQos.equals(other.mDefaultQos);
 
-        final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
-                mQosSessions == other.mQosSessions :
-                mQosSessions.size() == other.mQosSessions.size()
-                && mQosSessions.containsAll(other.mQosSessions);
+        final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
+                mQosBearerSessions == other.mQosBearerSessions :
+                mQosBearerSessions.size() == other.mQosBearerSessions.size()
+                && mQosBearerSessions.containsAll(other.mQosBearerSessions);
 
         return mCause == other.mCause
                 && mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -472,7 +472,7 @@
                 && mHandoverFailureMode == other.mHandoverFailureMode
                 && mPduSessionId == other.mPduSessionId
                 && isQosSame
-                && isQosSessionsSame
+                && isQosBearerSessionsSame
                 && Objects.equals(mSliceInfo, other.mSliceInfo);
     }
 
@@ -481,7 +481,7 @@
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
                 mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
-                mQosSessions, mSliceInfo);
+                mQosBearerSessions, mSliceInfo);
     }
 
     @Override
@@ -515,7 +515,7 @@
         } else {
             dest.writeParcelable(null, flags);
         }
-        dest.writeList(mQosSessions);
+        dest.writeList(mQosBearerSessions);
         dest.writeParcelable(mSliceInfo, flags);
     }
 
@@ -598,7 +598,7 @@
 
         private Qos mDefaultQos;
 
-        private List<QosSession> mQosSessions = new ArrayList<>();
+        private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
 
         private SliceInfo mSliceInfo;
 
@@ -812,15 +812,16 @@
         /**
          * Set the dedicated bearer QOS sessions for this data connection.
          *
-         * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+         * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received
          * from network.
          *
          * @return The same instance of the builder.
          *
          * @hide
          */
-        public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
-            mQosSessions = qosSessions;
+        public @NonNull Builder setQosBearerSessions(
+                @NonNull List<QosBearerSession> qosBearerSessions) {
+            mQosBearerSessions = qosBearerSessions;
             return this;
         }
 
@@ -848,7 +849,7 @@
             return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                     mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
                     mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
-                    mDefaultQos, mQosSessions, mSliceInfo);
+                    mDefaultQos, mQosBearerSessions, mSliceInfo);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
index ad43068..22c8b0a 100644
--- a/telephony/java/android/telephony/data/EpsQos.java
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -48,6 +48,10 @@
         qosClassId = source.readInt();
     }
 
+    public int getQci() {
+        return qosClassId;
+    }
+
     public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
         return new EpsQos(in);
     }
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
index c8bb91e..c286c621 100644
--- a/telephony/java/android/telephony/data/Qos.java
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -56,7 +56,15 @@
         this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
     }
 
-    static class QosBandwidth implements Parcelable {
+    public QosBandwidth getDownlinkBandwidth() {
+        return downlink;
+    }
+
+    public QosBandwidth getUplinkBandwidth() {
+        return uplink;
+    }
+
+    public static class QosBandwidth implements Parcelable {
         int maxBitrateKbps;
         int guaranteedBitrateKbps;
 
@@ -73,6 +81,14 @@
             guaranteedBitrateKbps = source.readInt();
         }
 
+        public int getMaxBitrateKbps() {
+            return maxBitrateKbps;
+        }
+
+        public int getGuaranteedBitrateKbps() {
+            return guaranteedBitrateKbps;
+        }
+
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(maxBitrateKbps);
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
similarity index 89%
rename from telephony/java/android/telephony/data/QosFilter.java
rename to telephony/java/android/telephony/data/QosBearerFilter.java
index 6927744..6c1c653 100644
--- a/telephony/java/android/telephony/data/QosFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -38,7 +38,7 @@
  *
  * @hide
  */
-public final class QosFilter implements Parcelable {
+public final class QosBearerFilter implements Parcelable {
 
     private List<LinkAddress> localAddresses;
     private List<LinkAddress> remoteAddresses;
@@ -74,7 +74,7 @@
     @IntDef(prefix = "QOS_FILTER_DIRECTION_",
             value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
                     QOS_FILTER_DIRECTION_BIDIRECTIONAL})
-    public @interface QosFilterDirection {}
+    public @interface QosBearerFilterDirection {}
 
     public static final int QOS_FILTER_DIRECTION_DOWNLINK =
             android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
@@ -83,7 +83,7 @@
     public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
             android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
 
-    @QosFilterDirection
+    @QosBearerFilterDirection
     private int filterDirection;
 
     /**
@@ -92,7 +92,7 @@
      */
     private int precedence;
 
-    QosFilter() {
+    QosBearerFilter() {
         localAddresses = new ArrayList<>();
         remoteAddresses = new ArrayList<>();
         localPort = new PortRange();
@@ -101,7 +101,7 @@
         filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
     }
 
-    public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+    public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
             PortRange localPort, PortRange remotePort, int protocol, int tos,
             long flowLabel, long spi, int direction, int precedence) {
         this.localAddresses = localAddresses;
@@ -116,10 +116,30 @@
         this.precedence = precedence;
     }
 
+    public List<LinkAddress> getLocalAddresses() {
+        return localAddresses;
+    }
+
+    public List<LinkAddress> getRemoteAddresses() {
+        return remoteAddresses;
+    }
+
+    public PortRange getLocalPortRange() {
+        return localPort;
+    }
+
+    public PortRange getRemotePortRange() {
+        return remotePort;
+    }
+
+    public int getPrecedence() {
+        return precedence;
+    }
+
     /** @hide */
-    public static @NonNull QosFilter create(
+    public static @NonNull QosBearerFilter create(
             @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
-        QosFilter ret = new QosFilter();
+        QosBearerFilter ret = new QosBearerFilter();
 
         String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
         if (localAddresses != null) {
@@ -202,6 +222,14 @@
             this.end = end;
         }
 
+        public int getStart() {
+            return start;
+        }
+
+        public int getEnd() {
+            return end;
+        }
+
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(start);
@@ -254,7 +282,7 @@
 
     @Override
     public String toString() {
-        return "QosFilter {"
+        return "QosBearerFilter {"
                 + " localAddresses=" + localAddresses
                 + " remoteAddresses=" + remoteAddresses
                 + " localPort=" + localPort
@@ -278,11 +306,11 @@
     public boolean equals(Object o) {
         if (this == o) return true;
 
-        if (o == null || !(o instanceof QosFilter)) {
+        if (o == null || !(o instanceof QosBearerFilter)) {
             return false;
         }
 
-        QosFilter other = (QosFilter) o;
+        QosBearerFilter other = (QosBearerFilter) o;
 
         return localAddresses.size() == other.localAddresses.size()
                 && localAddresses.containsAll(other.localAddresses)
@@ -324,7 +352,7 @@
                 LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
     }
 
-    private QosFilter(Parcel source) {
+    private QosBearerFilter(Parcel source) {
         localAddresses = new ArrayList<>();
         source.readList(localAddresses, LinkAddress.class.getClassLoader());
         remoteAddresses = new ArrayList<>();
@@ -358,16 +386,16 @@
         return 0;
     }
 
-    public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
-            new Parcelable.Creator<QosFilter>() {
+    public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR =
+            new Parcelable.Creator<QosBearerFilter>() {
                 @Override
-                public QosFilter createFromParcel(Parcel source) {
-                    return new QosFilter(source);
+                public QosBearerFilter createFromParcel(Parcel source) {
+                    return new QosBearerFilter(source);
                 }
 
                 @Override
-                public QosFilter[] newArray(int size) {
-                    return new QosFilter[size];
+                public QosBearerFilter[] newArray(int size) {
+                    return new QosBearerFilter[size];
                 }
             };
 }
diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java
new file mode 100644
index 0000000..30effc9
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosBearerSession.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosBearerSession implements Parcelable{
+
+    final int qosBearerSessionId;
+    final Qos qos;
+    final List<QosBearerFilter> qosBearerFilterList;
+
+    public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) {
+        this.qosBearerSessionId = qosBearerSessionId;
+        this.qos = qos;
+        this.qosBearerFilterList = qosBearerFilterList;
+    }
+
+    private QosBearerSession(Parcel source) {
+        qosBearerSessionId = source.readInt();
+        qos = source.readParcelable(Qos.class.getClassLoader());
+        qosBearerFilterList = new ArrayList<>();
+        source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader());
+    }
+
+    public int getQosBearerSessionId() {
+        return qosBearerSessionId;
+    }
+
+    public Qos getQos() {
+        return qos;
+    }
+
+    public List<QosBearerFilter> getQosBearerFilterList() {
+        return qosBearerFilterList;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(qosBearerSessionId);
+        if (qos.getType() == Qos.QOS_TYPE_EPS) {
+            dest.writeParcelable((EpsQos)qos, flags);
+        } else {
+            dest.writeParcelable((NrQos)qos, flags);
+        }
+        dest.writeList(qosBearerFilterList);
+    }
+
+    public static @NonNull QosBearerSession create(
+            @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+        List<QosBearerFilter> qosBearerFilters = new ArrayList<>();
+
+        if (qosSession.qosFilters != null) {
+            for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+                qosBearerFilters.add(QosBearerFilter.create(filter));
+            }
+        }
+
+        return new QosBearerSession(
+                        qosSession.qosSessionId,
+                        Qos.create(qosSession.qos),
+                        qosBearerFilters);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "QosBearerSession {"
+                + " qosBearerSessionId=" + qosBearerSessionId
+                + " qos=" + qos
+                + " qosBearerFilterList=" + qosBearerFilterList + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof QosBearerSession)) {
+            return false;
+        }
+
+        QosBearerSession other = (QosBearerSession) o;
+        return this.qosBearerSessionId == other.qosBearerSessionId
+                && this.qos.equals(other.qos)
+                && this.qosBearerFilterList.size() == other.qosBearerFilterList.size()
+                && this.qosBearerFilterList.containsAll(other.qosBearerFilterList);
+    }
+
+
+    public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR =
+            new Parcelable.Creator<QosBearerSession>() {
+                @Override
+                public QosBearerSession createFromParcel(Parcel source) {
+                    return new QosBearerSession(source);
+                }
+
+                @Override
+                public QosBearerSession[] newArray(int size) {
+                    return new QosBearerSession[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
deleted file mode 100644
index f07b6a9..0000000
--- a/telephony/java/android/telephony/data/QosSession.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-
-/**
- * Class that stores information specific to QOS session.
- *
- * @hide
- */
-public final class QosSession implements Parcelable{
-
-    final int qosSessionId;
-    final Qos qos;
-    final List<QosFilter> qosFilterList;
-
-    public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
-        this.qosSessionId = qosSessionId;
-        this.qos = qos;
-        this.qosFilterList = qosFilterList;
-    }
-
-    private QosSession(Parcel source) {
-        qosSessionId = source.readInt();
-        qos = source.readParcelable(Qos.class.getClassLoader());
-        qosFilterList = new ArrayList<>();
-        source.readList(qosFilterList, QosFilter.class.getClassLoader());
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(qosSessionId);
-        if (qos.getType() == Qos.QOS_TYPE_EPS) {
-            dest.writeParcelable((EpsQos)qos, flags);
-        } else {
-            dest.writeParcelable((NrQos)qos, flags);
-        }
-        dest.writeList(qosFilterList);
-    }
-
-    public static @NonNull QosSession create(
-            @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
-        List<QosFilter> qosFilters = new ArrayList<>();
-
-        if (qosSession.qosFilters != null) {
-            for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
-                qosFilters.add(QosFilter.create(filter));
-            }
-        }
-
-        return new QosSession(
-                        qosSession.qosSessionId,
-                        Qos.create(qosSession.qos),
-                        qosFilters);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public String toString() {
-        return "QosSession {"
-                + " qosSessionId=" + qosSessionId
-                + " qos=" + qos
-                + " qosFilterList=" + qosFilterList + "}";
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(qosSessionId, qos, qosFilterList);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-
-        if (o == null || !(o instanceof QosSession)) {
-            return false;
-        }
-
-        QosSession other = (QosSession) o;
-        return this.qosSessionId == other.qosSessionId
-                && this.qos.equals(other.qos)
-                && this.qosFilterList.size() == other.qosFilterList.size()
-                && this.qosFilterList.containsAll(other.qosFilterList);
-    }
-
-
-    public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
-            new Parcelable.Creator<QosSession>() {
-                @Override
-                public QosSession createFromParcel(Parcel source) {
-                    return new QosSession(source);
-                }
-
-                @Override
-                public QosSession[] newArray(int size) {
-                    return new QosSession[size];
-                }
-            };
-}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index f39e30b..814ce18 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -39,6 +40,8 @@
 
 import com.android.internal.telephony.IIntegerConsumer;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -77,55 +80,13 @@
             "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
 
     /**
-     * Receives RCS Feature availability status updates from the ImsService.
-     *
-     * @see #isAvailable(int)
-     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
-     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * An application can use {@link #addOnAvailabilityChangedListener} to register a
+     * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
+     * availability status updates from the ImsService.
      * @hide
      */
-    public static class AvailabilityCallback {
-
-        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
-
-            private final AvailabilityCallback mLocalCallback;
-            private Executor mExecutor;
-
-            CapabilityBinder(AvailabilityCallback c) {
-                mLocalCallback = c;
-            }
-
-            @Override
-            public void onCapabilitiesStatusChanged(int config) {
-                if (mLocalCallback == null) return;
-
-                final long callingIdentity = Binder.clearCallingIdentity();
-                try {
-                    mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config));
-                } finally {
-                    restoreCallingIdentity(callingIdentity);
-                }
-            }
-
-            @Override
-            public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                    boolean isEnabled) {
-                // This is not used for public interfaces.
-            }
-
-            @Override
-            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                    @ImsFeature.ImsCapabilityError int reason) {
-                // This is not used for public interfaces
-            }
-
-            private void setExecutor(Executor executor) {
-                mExecutor = executor;
-            }
-        }
-
-        private final CapabilityBinder mBinder = new CapabilityBinder(this);
-
+    @SystemApi
+    public interface OnAvailabilityChangedListener {
         /**
          * The availability of the feature's capabilities has changed to either available or
          * unavailable.
@@ -136,22 +97,68 @@
          *
          * @param capabilities The new availability of the capabilities.
          */
-        public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+    }
+
+    /**
+     * Receive the availability status changed from the ImsService and pass the status change to
+     * the associated {@link OnAvailabilityChangedListener}
+     */
+    private static class AvailabilityCallbackAdapter {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+            private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
+            private final Executor mExecutor;
+
+            CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
+                mExecutor = executor;
+                mOnAvailabilityChangedListener = listener;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mOnAvailabilityChangedListener == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mOnAvailabilityChangedListener.onAvailabilityChanged(config));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used.
+            }
+        }
+
+        private final CapabilityBinder mBinder;
+
+        AvailabilityCallbackAdapter(@NonNull Executor executor,
+                @NonNull OnAvailabilityChangedListener listener) {
+            mBinder = new CapabilityBinder(listener, executor);
         }
 
         /**@hide*/
         public final IImsCapabilityCallback getBinder() {
             return mBinder;
         }
-
-        private void setExecutor(Executor executor) {
-            mBinder.setExecutor(executor);
-        }
     }
 
     private final int mSubId;
     private final Context mContext;
     private final BinderCacheManager<IImsRcsController> mBinderCache;
+    private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
+            mAvailabilityChangedCallbacks;
 
     /**
      * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
@@ -162,6 +169,7 @@
         mSubId = subId;
         mContext = context;
         mBinderCache = binderCache;
+        mAvailabilityChangedCallbacks = new HashMap<>();
     }
 
     /**
@@ -174,10 +182,23 @@
     }
 
     /**
-     * @hide
+     * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
+     * callback is registered, it will initiate the callback c to be called with the current
+     * registration state.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
+     * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void registerImsRegistrationCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RegistrationManager.RegistrationCallback c)
@@ -191,7 +212,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Register registration callback: IImsRcsController is null");
+            Log.w(TAG, "Register registration callback: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -207,10 +228,21 @@
     }
 
     /**
-     * @hide
+     * Removes an existing {@link RegistrationManager.RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
             @NonNull RegistrationManager.RegistrationCallback c) {
         if (c == null) {
@@ -219,7 +251,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+            Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -231,10 +263,21 @@
     }
 
     /**
-     * @hide
+     * Gets the registration state of the IMS service.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the IMS registration state
+     * callback.
+     * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+     * registration state of the IMS service, which will be one of the
+     * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
             @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
         if (stateCallback == null) {
@@ -246,7 +289,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Get registration state error: IImsRcsController is null");
+            Log.w(TAG, "Get registration state error: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -263,9 +306,20 @@
     }
 
     /**
-     * @hide
+     * Gets the Transport Type associated with the current IMS registration.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+     * @param transportTypeCallback The transport type associated with the current IMS registration,
+     * which will be one of following:
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
      */
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
             @NonNull @AccessNetworkConstants.TransportType
                     Consumer<Integer> transportTypeCallback) {
@@ -278,7 +332,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+            Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -296,31 +350,33 @@
     }
 
     /**
-     * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+     * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
      * availability updates for the subscription specified.
      *
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
-     * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
-     * subscription is removed.
+     * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
+     * after a subscription is removed.
      * <p>
-     * When the callback is registered, it will initiate the callback c to be called with the
-     * current capabilities.
+     * When the listener is registered, it will initiate the callback listener to be called with
+     * the current capabilities.
      *
      * @param executor The executor the callback events should be run on.
-     * @param c The RCS {@link AvailabilityCallback} to be registered.
-     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
+     * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
      * @throws ImsException if the subscription associated with this instance of
      * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull AvailabilityCallback c) throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+    public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAvailabilityChangedListener listener) throws ImsException {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
         }
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
@@ -328,56 +384,61 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Register availability callback: IImsRcsController is null");
+            Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
-        c.setExecutor(executor);
+        AvailabilityCallbackAdapter adapter =
+                addAvailabilityChangedListenerToCollection(executor, listener);
         try {
-            imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
-
+            imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.toString(), e.errorCode);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
+            Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
-    /**
-     * Removes an existing RCS {@link AvailabilityCallback}.
+     /**
+     * Removes an existing RCS {@link OnAvailabilityChangedListener}.
      * <p>
      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
      * etc...), this callback will automatically be unregistered. If this method is called for an
      * inactive subscription, it will result in a no-op.
-     * @param c The RCS {@link AvailabilityCallback} to be removed.
-     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
+     * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
-            throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+    public void removeOnAvailabilityChangedListener(
+            @NonNull OnAvailabilityChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
         }
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
-            throw new ImsException("Cannot find remote IMS service",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
+            return;
+        }
+
+        AvailabilityCallbackAdapter callback =
+                removeAvailabilityChangedListenerFromCollection(listener);
+        if (callback == null) {
+            return;
         }
 
         try {
-            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
+            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
-            throw new ImsException("Remote IMS Service is not available",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
         }
     }
 
@@ -388,26 +449,24 @@
      * RCS capabilities provided over-the-top by applications.
      *
      * @param capability The RCS capability to query.
-     * @param radioTech The radio tech that this capability failed for, defined as
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+     * @param radioTech The radio technology type that we are querying.
      * @return true if the RCS capability is capable for this subscription, false otherwise. This
      * does not necessarily mean that we are registered for IMS and the capability is available, but
      * rather the subscription is capable of this service over IMS.
-     * @see #isAvailable(int)
+     * @see #isAvailable(int, int)
      * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
      * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "isCapable: IImsRcsController is null");
+            Log.w(TAG, "isCapable: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -415,7 +474,7 @@
         try {
             return imsRcsController.isCapable(mSubId, capability, radioTech);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
+            Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -428,6 +487,7 @@
      * RCS capabilities provided by over-the-top by applications.
      *
      * @param capability the RCS capability to query.
+     * @param radioTech The radio technology type that we are querying.
      * @return true if the RCS capability is currently available for the associated subscription,
      * false otherwise. If the capability is available, IMS is registered and the service is
      * currently available over IMS.
@@ -436,25 +496,57 @@
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability)
+    public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
             throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "isAvailable: IImsRcsController is null");
+            Log.w(TAG, "isAvailable: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         try {
-            return imsRcsController.isAvailable(mSubId, capability);
+            return imsRcsController.isAvailable(mSubId, capability, radioTech);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#isAvailable", e);
+            Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
+    /**
+     * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
+     * @param executor The executor that will be used when the publish state is changed and the
+     * {@link OnAvailabilityChangedListener} is called.
+     * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
+     * @return The {@link AvailabilityCallbackAdapter} to wrapper the
+     * {@link OnAvailabilityChangedListener}
+     */
+    private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
+            @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
+        AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
+        synchronized (mAvailabilityChangedCallbacks) {
+            mAvailabilityChangedCallbacks.put(listener, adapter);
+        }
+        return adapter;
+    }
+
+    /**
+     * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
+     * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
+     * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
+     * {@link OnAvailabilityChangedListener}.
+     */
+    private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
+            @NonNull OnAvailabilityChangedListener listener) {
+        synchronized (mAvailabilityChangedCallbacks) {
+            return mAvailabilityChangedCallbacks.remove(listener);
+        }
+    }
+
     private IImsRcsController getIImsRcsController() {
         IBinder binder = TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
diff --git a/tools/hiddenapi/Android.bp b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
similarity index 68%
rename from tools/hiddenapi/Android.bp
rename to telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
index e0eb06cb..0830ff2 100644
--- a/tools/hiddenapi/Android.bp
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,17 +14,6 @@
  * limitations under the License.
  */
 
-python_binary_host {
-	name: "merge_csv",
-	main: "merge_csv.py",
-	srcs: ["merge_csv.py"],
-	version: {
-		py2: {
-			enabled: false,
-		},
-		py3: {
-			enabled: true,
-			embedded_launcher: true
-		},
-	},
-}
+package android.telephony.ims;
+
+parcelable ImsRegistrationAttributes;
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
new file mode 100644
index 0000000..ccb3231
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -0,0 +1,252 @@
+/*
+ * 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the attributes associated with the current IMS registration.
+ */
+public final class ImsRegistrationAttributes implements Parcelable {
+
+    /**
+     * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN.
+     * <p>
+     * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this
+     * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set.
+     *
+     */
+    public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0;
+
+    /** @hide */
+    // Defines the underlying radio technology type that we have registered for IMS over.
+    @IntDef(prefix = "ATTR_",
+            value = {
+                    ATTR_EPDG_OVER_CELL_INTERNET,
+            },
+            flag = true)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsAttributeFlag {}
+
+    /**
+     * Builder for creating {@link ImsRegistrationAttributes} instances.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private final int mRegistrationTech;
+        private Set<String> mFeatureTags = Collections.emptySet();
+
+        /**
+         * Build a new instance of {@link ImsRegistrationAttributes}.
+         *
+         * @param registrationTech The Radio Access Technology that IMS is registered on.
+         */
+        public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
+            mRegistrationTech = registrationTech;
+        }
+
+        /**
+         * Optional IMS feature tags included in this IMS registration.
+         * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with
+         *         the IMS registration. This information is used for services such as the UCE
+         *         service to ascertain the complete IMS registration state to ensure the SIP
+         *         PUBLISH is accurate. The format of the set of feature tags must be one feature
+         *         tag key and value per entry. Each feature tag will contain the feature tag name
+         *         and string value (if applicable), even if they have the same feature tag name.
+         *         For example,
+         *         {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+         *         urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must
+         *         be split into three feature tag entries:
+         *         {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+         *         +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+         *         +g.gsma.callcomposer}}.
+         */
+        public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) {
+            if (tags == null) {
+                throw new IllegalArgumentException("feature tag set must not be null");
+            }
+            mFeatureTags = new ArraySet<>(tags);
+            return this;
+        }
+
+        /**
+         * @return A new instance created from this builder.
+         */
+        public @NonNull ImsRegistrationAttributes build() {
+            return new ImsRegistrationAttributes(mRegistrationTech,
+                    RegistrationManager.getAccessType(mRegistrationTech),
+                    getAttributeFlags(mRegistrationTech),
+                    mFeatureTags);
+        }
+
+        /**
+         * @return attribute flags from the registration technology.
+         */
+        private static int getAttributeFlags(int imsRadioTech) {
+            int attributes = 0;
+            if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+                attributes |= ATTR_EPDG_OVER_CELL_INTERNET;
+            }
+            return attributes;
+        }
+    }
+
+    private final int mRegistrationTech;
+    private final int mTransportType;
+    private final int mImsAttributeFlags;
+    private final ArrayList<String> mFeatureTags;
+
+    /**
+     * Create a new {@link ImsRegistrationAttributes} instance.
+     *
+     * @param registrationTech The technology that IMS has been registered on.
+     * @param transportType The transport type that IMS has been registered on.
+     * @param imsAttributeFlags The attributes associated with the IMS registration.
+     * @param featureTags The feature tags included in the IMS registration.
+     * @see Builder
+     * @hide
+     */
+    public ImsRegistrationAttributes(
+            @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+            @AccessNetworkConstants.TransportType int transportType,
+            @ImsAttributeFlag int imsAttributeFlags,
+            @Nullable Set<String> featureTags) {
+        mRegistrationTech = registrationTech;
+        mTransportType = transportType;
+        mImsAttributeFlags = imsAttributeFlags;
+        mFeatureTags = new ArrayList<>(featureTags);
+    }
+
+    /**@hide*/
+    public ImsRegistrationAttributes(Parcel source) {
+        mRegistrationTech = source.readInt();
+        mTransportType = source.readInt();
+        mImsAttributeFlags = source.readInt();
+        mFeatureTags = new ArrayList<>();
+        source.readList(mFeatureTags, null /*classloader*/);
+    }
+
+    /**
+     * @return The Radio Access Technology that the IMS registration has been registered over.
+     * @hide
+     */
+    @SystemApi
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() {
+        return mRegistrationTech;
+    }
+
+    /**
+     * @return The access network transport type that IMS has been registered over.
+     */
+    public @AccessNetworkConstants.TransportType int  getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * @return A bit-mask containing attributes associated with the IMS registration.
+     */
+    public @ImsAttributeFlag int getAttributeFlags() {
+        return mImsAttributeFlags;
+    }
+
+    /**
+     * Gets the Set of feature tags associated with the current IMS registration, if the IMS
+     * service supports supplying this information.
+     * <p>
+     * The format of the set of feature tags will be one feature tag key and value per entry and
+     * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS
+     * service associated with the registration indications. Each feature tag will contain the
+     * feature tag name and string value (if applicable), even if they have the same feature tag
+     * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+     * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split
+     * into three feature tag  entries:
+     * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+     * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+     * +g.gsma.callcomposer}}.
+     * @return The Set of feature tags associated with the current IMS registration.
+     */
+    public @NonNull Set<String> getFeatureTags() {
+        if (mFeatureTags == null) {
+            return Collections.emptySet();
+        }
+        return new ArraySet<>(mFeatureTags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mRegistrationTech);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mImsAttributeFlags);
+        dest.writeList(mFeatureTags);
+    }
+
+    public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR =
+            new Creator<ImsRegistrationAttributes>() {
+                @Override
+                public ImsRegistrationAttributes createFromParcel(Parcel source) {
+                    return new ImsRegistrationAttributes(source);
+                }
+
+                @Override
+                public ImsRegistrationAttributes[] newArray(int size) {
+                    return new ImsRegistrationAttributes[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ImsRegistrationAttributes that = (ImsRegistrationAttributes) o;
+        return mRegistrationTech == that.mRegistrationTech
+                && mTransportType == that.mTransportType
+                && mImsAttributeFlags == that.mImsAttributeFlags
+                && Objects.equals(mFeatureTags, that.mFeatureTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags);
+    }
+
+    @Override
+    public String toString() {
+        return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags="
+                + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}";
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index b430bef..c682afe 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -72,30 +72,6 @@
      */
     int REGISTRATION_STATE_REGISTERED = 2;
 
-    /**
-     * @hide
-     */
-    // Defines the underlying radio technology type that we have registered for IMS over.
-    @IntDef(prefix = "ATTR_",
-            value = {
-                    ATTR_EPDG_OVER_CELL_INTERNET,
-            },
-            flag = true)
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsAttributes {}
-
-    /**
-     * Attribute to specify if EPDG tunnel is setup over cellular internet.
-     * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will
-     * not be set.
-     */
-    int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001;
-
-    //******************************************************************************************
-    // Next attribute value: 0x00000002
-    //******************************************************************************************
-
-
     /**@hide*/
     // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
     // and WWAN are more accurate constants.
@@ -103,7 +79,8 @@
             new HashMap<Integer, Integer>() {{
                 // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
                 // case, since it is defined.
-                put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+                put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
+                        AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
                 put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
                 put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
@@ -132,6 +109,20 @@
     }
 
     /**
+     * @param regtech The registration technology.
+     * @return The Access Network type from registration technology.
+     * @hide
+     */
+    static int getAccessType(int regtech) {
+        if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) {
+            Log.w("RegistrationManager", "getAccessType - invalid regType returned: "
+                    + regtech);
+            return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        }
+        return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech);
+    }
+
+    /**
      * Callback class for receiving IMS network Registration callback events.
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
@@ -149,45 +140,24 @@
             }
 
             @Override
-            public void onRegistered(int imsRadioTech) {
+            public void onRegistered(ImsRegistrationAttributes attr) {
                 if (mLocalCallback == null) return;
 
                 final long callingIdentity = Binder.clearCallingIdentity();
                 try {
-                    mExecutor.execute(() -> {
-                        mLocalCallback.onRegistered(getAccessType(imsRadioTech));
-                    });
-                    int attributes = 0;
-                    if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-                        attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
-                                true);
-                    }
-                    final int finalattributes = attributes;
-                    mExecutor.execute(() ->
-                            mLocalCallback.onRegistered(getAccessType(imsRadioTech),
-                                    finalattributes));
+                    mExecutor.execute(() -> mLocalCallback.onRegistered(attr));
                 } finally {
                     restoreCallingIdentity(callingIdentity);
                 }
             }
 
             @Override
-            public void onRegistering(int imsRadioTech) {
+            public void onRegistering(ImsRegistrationAttributes attr) {
                 if (mLocalCallback == null) return;
 
                 final long callingIdentity = Binder.clearCallingIdentity();
                 try {
-                    mExecutor.execute(() ->
-                            mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
-                    int attributes = 0;
-                    if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
-                        attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
-                                true);
-                    }
-                    final int finalattributes = attributes;
-                    mExecutor.execute(() ->
-                            mLocalCallback.onRegistering(getAccessType(imsRadioTech),
-                                    finalattributes));
+                    mExecutor.execute(() -> mLocalCallback.onRegistering(attr));
                 } finally {
                     restoreCallingIdentity(callingIdentity);
                 }
@@ -232,31 +202,6 @@
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
-
-            private static int getAccessType(int regType) {
-                if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
-                    Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: "
-                            + regType);
-                    return -1;
-                }
-                return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
-            }
-
-            /**
-             * Changes a attribute bit-mask to add or remove an attribute.
-             *
-             * @param bitmask The bit-mask.
-             * @param bitfield The bit-field to change.
-             * @param enabled Whether the bit-field should be set or removed.
-             * @return The bit-mask with the bit-field changed.
-             */
-            private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
-                if (enabled) {
-                    return bitmask | bitfield;
-                } else {
-                    return bitmask & ~bitfield;
-                }
-            }
         }
 
         private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -265,7 +210,7 @@
          * Notifies the framework when the IMS Provider is registered to the IMS network.
          *
          * @param imsTransportType the radio access technology.
-         * @deprecated Use {@link #onRegistered(int, int)} instead.
+         * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead.
          */
         @Deprecated
         public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
@@ -273,25 +218,20 @@
 
         /**
          * Notifies the framework when the IMS Provider is registered to the IMS network
-         * with corresponding attributes
+         * with corresponding attributes.
          *
-         * @param imsTransportType the radio access technology.
-         * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
-         * Possible attributes are following
-         * <ul>
-         *     <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li>
-         * </ul>
-         *
+         * @param attributes The attributes associated with this IMS registration.
          */
-        public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType,
-                @ImsAttributes int registrationAttributes) {
+        public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+            // Default impl to keep backwards compatibility with old implementations
+            onRegistered(attributes.getTransportType());
         }
 
         /**
          * Notifies the framework when the IMS Provider is trying to register the IMS network.
          *
          * @param imsTransportType the radio access technology.
-         * @deprecated Use {@link #onRegistering(int, int)} instead.
+         * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead.
          */
         public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
         }
@@ -299,12 +239,11 @@
         /**
          * Notifies the framework when the IMS Provider is trying to register the IMS network.
          *
-         * @param imsTransportType the radio access technology.
-         * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
-         * Possible attributes are following
+         * @param attributes The attributes associated with this IMS registration.
          */
-        public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType,
-                @ImsAttributes int registrationAttributes) {
+        public void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+            // Default impl to keep backwards compatibility with old implementations
+            onRegistering(attributes.getTransportType());
         }
 
         /**
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 7a6c28b..8931a78 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -47,7 +47,7 @@
     void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     boolean isCapable(int subId, int capability, int radioTech);
-    boolean isAvailable(int subId, int capability);
+    boolean isAvailable(int subId, int capability, int radioTech);
 
     // ImsUceAdapter specific
     void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 749b191..179407c 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -21,6 +21,7 @@
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 
 /**
  * See {@link ImsManager#RegistrationCallback} for more information.
@@ -28,8 +29,8 @@
  * {@hide}
  */
 oneway interface IImsRegistrationCallback {
-   void onRegistered(int imsRadioTech);
-   void onRegistering(int imsRadioTech);
+   void onRegistered(in ImsRegistrationAttributes attr);
+   void onRegistering(in ImsRegistrationAttributes attr);
    void onDeregistered(in ImsReasonInfo info);
    void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
    void onSubscriberAssociatedUriChanged(in Uri[] uris);
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index c5b1c90..f3791d1 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -36,12 +36,9 @@
 public final class CapabilityChangeRequest implements Parcelable {
 
     /**
-     * Contains a feature capability, defined as
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
-     * along with an associated technology, defined as
+     * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature
+     * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology,
+     * defined as
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
@@ -50,7 +47,7 @@
         private final int mCapability;
         private final int radioTech;
 
-        public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+        public CapabilityPair(int capability,
                 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
             this.mCapability = capability;
             this.radioTech = radioTech;
@@ -81,13 +78,10 @@
         }
 
         /**
-         * @return The stored capability, defined as
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+         * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and
+         * {@link RcsFeature.RcsImsCapabilities}
          */
-        public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+        public int getCapability() {
             return mCapability;
         }
 
@@ -125,12 +119,11 @@
      * Add one or many capabilities to the request to be enabled.
      *
      * @param capabilities A bitfield of capabilities to enable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
      * @param radioTech  the radio tech that these capabilities should be enabled for, valid
      *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
      */
-    public void addCapabilitiesToEnableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+    public void addCapabilitiesToEnableForTech(int capabilities,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
     }
@@ -138,12 +131,11 @@
     /**
      * Add one or many capabilities to the request to be disabled.
      * @param capabilities A bitfield of capabilities to diable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
      * @param radioTech the radio tech that these capabilities should be disabled for, valid
      *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
      */
-    public void addCapabilitiesToDisableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+    public void addCapabilitiesToDisableForTech(int capabilities,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
     }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 22df921..85703f8 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -194,7 +194,6 @@
      * of the capability and notify the capability status as true using
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
-     * @hide
      */
     public static class RcsImsCapabilities extends Capabilities {
         /** @hide*/
@@ -226,12 +225,21 @@
          */
         public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
 
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for RCS in the form of a
+         * bitfield.
+         */
         public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             super(capabilities);
         }
 
-        private RcsImsCapabilities(Capabilities c) {
-            super(c.getMask());
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities instance that are supported for RCS
+         */
+        private RcsImsCapabilities(Capabilities capabilities) {
+            super(capabilities.getMask());
         }
 
         @Override
@@ -307,7 +315,7 @@
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
-     * @hide
+     * @return A copy of the current RcsFeature capability status.
      */
     @Override
     public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -318,13 +326,13 @@
      * Notify the framework that the capabilities status has changed. If a capability is enabled,
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
-     * @hide
+     * @param capabilities The current capability status of the RcsFeature.
      */
-    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
-        if (c == null) {
+    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
+        if (capabilities == null) {
             throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
         }
-        super.notifyCapabilitiesStatusChanged(c);
+        super.notifyCapabilitiesStatusChanged(capabilities);
     }
 
     /**
@@ -333,7 +341,9 @@
      * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
      * enable or disable capability A, this method should return the correct configuration for
      * capability A afterwards (until it has changed).
-     * @hide
+     * @param capability The capability that we are querying the configuration for.
+     * @param radioTech The radio technology type that we are querying.
+     * @return true if the capability is enabled, false otherwise.
      */
     public boolean queryCapabilityConfiguration(
             @RcsUceAdapter.RcsImsCapabilityFlag int capability,
@@ -355,11 +365,12 @@
      * If for some reason one or more of these capabilities can not be enabled/disabled,
      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
      * be called for each capability change that resulted in an error.
-     * @hide
+     * @param request The request to change the capability.
+     * @param callback To notify the framework that the result of the capability changes.
      */
     @Override
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
-            @NonNull CapabilityCallbackProxy c) {
+            @NonNull CapabilityCallbackProxy callback) {
         // Base Implementation - Override to provide functionality
     }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 4f753c3..39994be 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -18,17 +18,18 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.util.RemoteCallbackListExt;
 import com.android.internal.util.ArrayUtils;
 
@@ -89,7 +90,10 @@
 
         @Override
         public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
-            return getConnectionType();
+            synchronized (mLock) {
+                return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
+                        : mRegistrationAttributes.getRegistrationTechnology();
+            }
         }
 
         @Override
@@ -122,8 +126,7 @@
             new RemoteCallbackListExt<>();
     private final Object mLock = new Object();
     // Locked on mLock
-    private @ImsRegistrationTech
-    int mConnectionType = REGISTRATION_TECH_NONE;
+    private ImsRegistrationAttributes mRegistrationAttributes;
     // Locked on mLock
     private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
     // Locked on mLock, create unspecified disconnect cause.
@@ -201,18 +204,24 @@
     /**
      * Notify the framework that the device is connected to the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined as
-     * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
-     * {@link #REGISTRATION_TECH_CROSS_SIM}.
+     * @param imsRadioTech the radio access technology.
      */
     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
-        updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+        onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+    }
+
+    /**
+     * Notify the framework that the device is connected to the IMS network.
+     *
+     * @param attributes The attributes associated with the IMS registration.
+     */
+    public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+        updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onRegistered(imsRadioTech);
+                c.onRegistered(attributes);
             } catch (RemoteException e) {
-                Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
-                        "callback.");
+                Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback.");
             }
         });
     }
@@ -220,18 +229,24 @@
     /**
      * Notify the framework that the device is trying to connect the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined as
-     * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
-     * {@link #REGISTRATION_TECH_CROSS_SIM}.
+     * @param imsRadioTech the radio access technology.
      */
     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
-        updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+        onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+    }
+
+    /**
+     * Notify the framework that the device is trying to connect the IMS network.
+     *
+     * @param attributes The attributes associated with the IMS registration.
+     */
+    public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+        updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onRegistering(imsRadioTech);
+                c.onRegistering(attributes);
             } catch (RemoteException e) {
-                Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
-                        "callback.");
+                Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback.");
             }
         });
     }
@@ -260,8 +275,7 @@
             try {
                 c.onDeregistered(reasonInfo);
             } catch (RemoteException e) {
-                Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
-                        "callback.");
+                Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
             }
         });
     }
@@ -281,8 +295,7 @@
             try {
                 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
             } catch (RemoteException e) {
-                Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
-                        "callback.");
+                Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback.");
             }
         });
     }
@@ -306,14 +319,13 @@
         try {
             callback.onSubscriberAssociatedUriChanged(uris);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping "
-                    + "callback.");
+            Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback.");
         }
     }
 
-    private void updateToState(@ImsRegistrationTech int connType, int newState) {
+    private void updateToState(ImsRegistrationAttributes attributes, int newState) {
         synchronized (mLock) {
-            mConnectionType = connType;
+            mRegistrationAttributes = attributes;
             mRegistrationState = newState;
             mLastDisconnectCause = null;
         }
@@ -325,7 +337,7 @@
             mUrisSet = false;
             mUris = null;
 
-            updateToState(REGISTRATION_TECH_NONE,
+            updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(),
                     RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
             if (info != null) {
                 mLastDisconnectCause = info;
@@ -337,30 +349,19 @@
     }
 
     /**
-     * @return the current registration connection type. Valid values are
-     * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
-     * {@link #REGISTRATION_TECH_CROSS_SIM}.
-     * @hide
-     */
-    @VisibleForTesting
-    public final @ImsRegistrationTech int getConnectionType() {
-        synchronized (mLock) {
-            return mConnectionType;
-        }
-    }
-
-    /**
      * @param c the newly registered callback that will be updated with the current registration
      *         state.
      */
     private void updateNewCallbackWithState(IImsRegistrationCallback c)
             throws RemoteException {
         int state;
+        ImsRegistrationAttributes attributes;
         ImsReasonInfo disconnectInfo;
         boolean urisSet;
         Uri[] uris;
         synchronized (mLock) {
             state = mRegistrationState;
+            attributes = mRegistrationAttributes;
             disconnectInfo = mLastDisconnectCause;
             urisSet = mUrisSet;
             uris = mUris;
@@ -371,11 +372,11 @@
                 break;
             }
             case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
-                c.onRegistering(getConnectionType());
+                c.onRegistering(attributes);
                 break;
             }
             case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
-                c.onRegistered(getConnectionType());
+                c.onRegistered(attributes);
                 break;
             }
             case REGISTRATION_STATE_UNKNOWN: {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0cd17da3..1d04953 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2387,4 +2387,18 @@
      * Gets the current phone capability.
      */
     PhoneCapability getPhoneCapability();
+
+    /**
+     * Prepare TelephonyManager for an unattended reboot. The reboot is
+     * required to be done shortly after the API is invoked.
+     *
+     * Requires system privileges.
+     *
+     * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+     * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+     * at least one SIM card for which the user needs to manually enter the PIN
+     * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+     * of error.
+     */
+    int prepareForUnattendedReboot();
 }
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 39dc9c2..e2d2eca 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,7 +16,10 @@
     name: "ApkVerityTest",
     srcs: ["src/**/*.java"],
     libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
-    static_libs: ["frameworks-base-hostutils"],
+    static_libs: [
+        "block_device_writer_jar",
+        "frameworks-base-hostutils",
+    ],
     test_suites: ["general-tests", "vts"],
     target_required: [
         "block_device_writer_module",
diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS
new file mode 100644
index 0000000..d67285ed
--- /dev/null
+++ b/tests/ApkVerityTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+victorhsieh@google.com
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 37fbc29..8f2d4bc 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -51,3 +51,9 @@
     test_suites: ["general-tests", "pts", "vts"],
     gtest: false,
 }
+
+java_library_host {
+    name: "block_device_writer_jar",
+    srcs: ["src/**/*.java"],
+    libs: ["tradefed", "junit"],
+}
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
new file mode 100644
index 0000000..5c2c15b
--- /dev/null
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -0,0 +1,100 @@
+/*
+ * 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.blockdevicewriter;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper for block_device_writer command.
+ *
+ * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
+ * 1. In Android.bp, add:
+ * <pre>
+ *     target_required: ["block_device_writer_module"],
+ * </pre>
+ * 2. In AndroidText.xml, add:
+ * <pre>
+ *     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ *         <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ *     </target_preparer>
+ * </pre>
+ */
+public final class BlockDeviceWriter {
+    private static final String EXECUTABLE = "/data/local/tmp/block_device_writer";
+
+    /**
+     * Modifies a byte of the file directly against the backing block storage.
+     *
+     * The effect can only be observed when the page cache is read from disk again. See
+     * {@link #dropCaches} for details.
+     */
+    public static void damageFileAgainstBlockDevice(ITestDevice device, String path,
+            long offsetOfTargetingByte)
+            throws DeviceNotAvailableException {
+        assertThat(path).startsWith("/data/");
+        ITestDevice.MountPointInfo mountPoint = device.getMountPointInfo("/data");
+        ArrayList<String> args = new ArrayList<>();
+        args.add(EXECUTABLE);
+        if ("f2fs".equals(mountPoint.type)) {
+            args.add("--use-f2fs-pinning");
+        }
+        args.add(mountPoint.filesystem);
+        args.add(path);
+        args.add(Long.toString(offsetOfTargetingByte));
+        CommandResult result = device.executeShellV2Command(String.join(" ", args));
+        assertWithMessage(
+                String.format("stdout=%s\nstderr=%s", result.getStdout(), result.getStderr()))
+                .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+    }
+
+    /**
+     * Drops file caches so that the result of {@link #damageFileAgainstBlockDevice} can be
+     * observed. If a process has an open FD or memory map of the damaged file, cache eviction won't
+     * happen and the damage cannot be observed.
+     */
+    public static void dropCaches(ITestDevice device) throws DeviceNotAvailableException {
+        CommandResult result = device.executeShellV2Command(
+                "sync && echo 1 > /proc/sys/vm/drop_caches");
+        assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+    }
+
+    public static void assertFileNotOpen(ITestDevice device, String path)
+            throws DeviceNotAvailableException {
+        CommandResult result = device.executeShellV2Command("lsof " + path);
+        assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+        assertThat(result.getStdout()).isEmpty();
+    }
+
+    /**
+     * Checks if the give offset of a file can be read.
+     * This method will return false if the file has fs-verity enabled and is damaged at the offset.
+     */
+    public static boolean canReadByte(ITestDevice device, String filePath, long offset)
+            throws DeviceNotAvailableException {
+        CommandResult result = device.executeShellV2Command(
+                "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
+        return result.getStatus() == CommandStatus.SUCCESS;
+    }
+}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index d0eb9be..ab3572b 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -24,6 +24,7 @@
 
 import android.platform.test.annotations.RootPermissionTest;
 
+import com.android.blockdevicewriter.BlockDeviceWriter;
 import com.android.fsverity.AddFsVerityCertRule;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -334,22 +335,23 @@
         long offsetFirstByte = 0;
 
         // The first two pages should be both readable at first.
-        assertTrue(canReadByte(apkPath, offsetFirstByte));
+        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
         if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
-            assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE));
+            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+                    offsetFirstByte + FSVERITY_PAGE_SIZE));
         }
 
         // Damage the file directly against the block device.
         damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
 
         // Expect actual read from disk to fail but only at damaged page.
-        dropCaches();
-        assertFalse(canReadByte(apkPath, offsetFirstByte));
+        BlockDeviceWriter.dropCaches(mDevice);
+        assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
         if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
             long lastByteOfTheSamePage =
                     offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
-            assertFalse(canReadByte(apkPath, lastByteOfTheSamePage));
-            assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1));
+            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
+            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
         }
     }
 
@@ -362,21 +364,22 @@
         long offsetOfLastByte = apkSize - 1;
 
         // The first two pages should be both readable at first.
-        assertTrue(canReadByte(apkPath, offsetOfLastByte));
+        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
         if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
-            assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE));
+            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+                    offsetOfLastByte - FSVERITY_PAGE_SIZE));
         }
 
         // Damage the file directly against the block device.
         damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
 
         // Expect actual read from disk to fail but only at damaged page.
-        dropCaches();
-        assertFalse(canReadByte(apkPath, offsetOfLastByte));
+        BlockDeviceWriter.dropCaches(mDevice);
+        assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
         if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
             long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
-            assertFalse(canReadByte(apkPath, firstByteOfTheSamePage));
-            assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1));
+            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
+            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
         }
     }
 
@@ -395,8 +398,8 @@
                 // from filesystem cache. Forcing GC workarounds the problem.
                 int retry = 5;
                 for (; retry > 0; retry--) {
-                    dropCaches();
-                    if (!canReadByte(path, kTargetOffset)) {
+                    BlockDeviceWriter.dropCaches(mDevice);
+                    if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
                         break;
                     }
                     try {
@@ -451,16 +454,6 @@
         return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
     }
 
-    private void dropCaches() throws DeviceNotAvailableException {
-        expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches");
-    }
-
-    private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException {
-        CommandResult result = mDevice.executeShellV2Command(
-                "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
-        return result.getStatus() == CommandStatus.SUCCESS;
-    }
-
     private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
         CommandResult result = mDevice.executeShellV2Command(cmd);
         assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index e68fbd8..b2719fb 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.wm.flicker"/>
-        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6600s" />
         <option name="hidden-api-checks" value="false" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 89c6663..c5447c1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,8 +17,11 @@
 package com.android.server.wm.flicker
 
 import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
 import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
 import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
@@ -31,115 +34,88 @@
 const val WALLPAPER_TITLE = "Wallpaper"
 
 @JvmOverloads
-fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
-        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) {
+    all("statusBarWindowIsAlwaysVisible", bugId) {
+        this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
     }
 }
 
 @JvmOverloads
-fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("navBarWindowIsAlwaysVisible", bugId, enabled) {
-        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) {
+    all("navBarWindowIsAlwaysVisible", bugId) {
+        this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
     }
 }
 
-fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
     ignoreWindows: List<String> = emptyList(),
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
-    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) {
         this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
     }
 }
 
-fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+    all("launcherReplacesAppWindowAsTopWindow", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
                 .then()
                 .showsAppWindowOnTop("Launcher")
     }
 }
 
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("wallpaperWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) {
+    all("wallpaperWindowBecomesVisible", bugId) {
         this.hidesBelowAppWindow(WALLPAPER_TITLE)
                 .then()
                 .showsBelowAppWindow(WALLPAPER_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) {
+    all("wallpaperWindowBecomesInvisible", bugId) {
         this.showsBelowAppWindow("Wallpaper")
                 .then()
                 .hidesBelowAppWindow("Wallpaper")
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
     packageName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
-    all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+    all("appWindowAlwaysVisibleOnTop", bugId) {
         this.showsAppWindowOnTop(packageName)
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
-    appName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) {
+    all("appWindowBecomesVisible", bugId) {
         this.hidesAppWindow(appName)
                 .then()
                 .showsAppWindow(appName)
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
-    appName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowBecomesInVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) {
+    all("appWindowBecomesInVisible", bugId) {
         this.showsAppWindow(appName)
-                .then()
-                .hidesAppWindow(appName)
+            .then()
+            .hidesAppWindow(appName)
     }
 }
 
 @JvmOverloads
-fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+fun LayersAssertionBuilder.noUncoveredRegions(
     beginRotation: Int,
     endRotation: Int = beginRotation,
     allStates: Boolean = true,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
     val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
     val endingBounds = WindowUtils.getDisplayBounds(endRotation)
     if (allStates) {
-        all("noUncoveredRegions", bugId, enabled) {
+        all("noUncoveredRegions", bugId) {
             if (startingBounds == endingBounds) {
                 this.coversAtLeastRegion(startingBounds)
             } else {
@@ -159,6 +135,284 @@
 }
 
 @JvmOverloads
+fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+    rotatesScreen: Boolean = false,
+    bugId: Int = 0
+) {
+    if (rotatesScreen) {
+        all("navBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(NAV_BAR_LAYER_NAME)
+                    .then()
+                    .hidesLayer(NAV_BAR_LAYER_NAME)
+                    .then()
+                    .showsLayer(NAV_BAR_LAYER_NAME)
+        }
+    } else {
+        all("navBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(NAV_BAR_LAYER_NAME)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+    rotatesScreen: Boolean = false,
+    bugId: Int = 0
+) {
+    if (rotatesScreen) {
+        all("statusBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+                    .then()
+                    hidesLayer(STATUS_BAR_WINDOW_NAME)
+                    .then()
+                    .showsLayer(STATUS_BAR_WINDOW_NAME)
+        }
+    } else {
+        all("statusBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0
+) {
+    val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+    val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+    start("navBarLayerRotatesAndScales_StartingPos", bugId) {
+        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
+    }
+    end("navBarLayerRotatesAndScales_EndingPost", bugId) {
+        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
+    }
+
+    /*if (startingPos == endingPos) {
+        all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
+            this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        }
+    }*/
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0
+) {
+    val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+    val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+    start("statusBarLayerRotatesScales_StartingPos", bugId) {
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
+    }
+    end("statusBarLayerRotatesScales_EndingPos", bugId) {
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
+    }
+}
+
+fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+    ignoreLayers: List<String> = emptyList(),
+    bugId: Int = 0
+) {
+    all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) {
+        this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
+    }
+}
+
+fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) {
+    all("appLayerReplacesWallpaperLayer", bugId) {
+        this.showsLayer("Wallpaper")
+                .then()
+                .replaceVisibleLayer("Wallpaper", appName)
+    }
+}
+
+fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) {
+    all("appLayerReplacesWallpaperLayer", bugId) {
+        this.showsLayer(testApp.getPackage())
+                .then()
+                .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+    }
+}
+
+fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) {
+    all("layerAlwaysVisible", bugId) {
+        this.showsLayer(packageName)
+    }
+}
+
+fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) {
+    all("layerBecomesVisible", bugId) {
+        this.hidesLayer(packageName)
+                .then()
+                .showsLayer(packageName)
+    }
+}
+
+fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) {
+    all("layerBecomesInvisible", bugId) {
+        this.showsLayer(packageName)
+                .then()
+                .hidesLayer(packageName)
+    }
+}
+
+fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) {
+    all("focusChanges", bugId) {
+        this.focusChanges(windows)
+    }
+}
+
+fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) {
+    all("focusDoesNotChange", bugId) {
+        this.focusDoesNotChange()
+    }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
+        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+    }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarWindowIsAlwaysVisible", bugId, enabled) {
+        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+    ignoreWindows: List<String> = emptyList(),
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+        this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+        this.showsAppWindowOnTop(testApp.getPackage())
+            .then()
+            .showsAppWindowOnTop("Launcher")
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesVisible", bugId, enabled) {
+        this.hidesBelowAppWindow(WALLPAPER_TITLE)
+            .then()
+            .showsBelowAppWindow(WALLPAPER_TITLE)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+        this.showsBelowAppWindow("Wallpaper")
+            .then()
+            .hidesBelowAppWindow("Wallpaper")
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+    packageName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+        this.showsAppWindowOnTop(packageName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
+    appName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowBecomesVisible", bugId, enabled) {
+        this.hidesAppWindow(appName)
+            .then()
+            .showsAppWindow(appName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
+    appName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowBecomesInVisible", bugId, enabled) {
+        this.showsAppWindow(appName)
+            .then()
+            .hidesAppWindow(appName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
+fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    allStates: Boolean = true,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+    val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+    if (allStates) {
+        all("noUncoveredRegions", bugId, enabled) {
+            if (startingBounds == endingBounds) {
+                this.coversAtLeastRegion(startingBounds)
+            } else {
+                this.coversAtLeastRegion(startingBounds)
+                    .then()
+                    .coversAtLeastRegion(endingBounds)
+            }
+        }
+    } else {
+        start("noUncoveredRegions_StartingPos") {
+            this.coversAtLeastRegion(startingBounds)
+        }
+        end("noUncoveredRegions_EndingPos") {
+            this.coversAtLeastRegion(endingBounds)
+        }
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
 fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
     rotatesScreen: Boolean = false,
     bugId: Int = 0,
@@ -167,10 +421,10 @@
     if (rotatesScreen) {
         all("navBarLayerIsAlwaysVisible", bugId, enabled) {
             this.showsLayer(NAV_BAR_LAYER_NAME)
-                    .then()
-                    .hidesLayer(NAV_BAR_LAYER_NAME)
-                    .then()
-                    .showsLayer(NAV_BAR_LAYER_NAME)
+                .then()
+                .hidesLayer(NAV_BAR_LAYER_NAME)
+                .then()
+                .showsLayer(NAV_BAR_LAYER_NAME)
         }
     } else {
         all("navBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -179,6 +433,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
     rotatesScreen: Boolean = false,
@@ -188,10 +443,10 @@
     if (rotatesScreen) {
         all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
             this.showsLayer(STATUS_BAR_LAYER_NAME)
-                    .then()
-                    hidesLayer(STATUS_BAR_LAYER_NAME)
-                    .then()
-                    .showsLayer(STATUS_BAR_LAYER_NAME)
+                .then()
+                .hidesLayer(STATUS_BAR_LAYER_NAME)
+                .then()
+                .showsLayer(STATUS_BAR_LAYER_NAME)
         }
     } else {
         all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -200,6 +455,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
     beginRotation: Int,
@@ -224,6 +480,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
     beginRotation: Int,
@@ -242,8 +499,9 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
-    ignoreLayers: List<String> = emptyList(),
+    ignoreLayers: List<String> = kotlin.collections.emptyList(),
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
 ) {
@@ -252,6 +510,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
     appName: String,
     bugId: Int = 0,
@@ -259,11 +518,12 @@
 ) {
     all("appLayerReplacesWallpaperLayer", bugId, enabled) {
         this.showsLayer("Wallpaper")
-                .then()
-                .replaceVisibleLayer("Wallpaper", appName)
+            .then()
+            .replaceVisibleLayer("Wallpaper", appName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
     testApp: IAppHelper,
     bugId: Int = 0,
@@ -271,11 +531,12 @@
 ) {
     all("appLayerReplacesWallpaperLayer", bugId, enabled) {
         this.showsLayer(testApp.getPackage())
-                .then()
-                .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+            .then()
+            .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
     packageName: String,
     bugId: Int = 0,
@@ -286,6 +547,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerBecomesVisible(
     packageName: String,
     bugId: Int = 0,
@@ -293,11 +555,12 @@
 ) {
     all("layerBecomesVisible", bugId, enabled) {
         this.hidesLayer(packageName)
-                .then()
-                .showsLayer(packageName)
+            .then()
+            .showsLayer(packageName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
     packageName: String,
     bugId: Int = 0,
@@ -305,11 +568,12 @@
 ) {
     all("layerBecomesInvisible", bugId, enabled) {
         this.showsLayer(packageName)
-                .then()
-                .hidesLayer(packageName)
+            .then()
+            .hidesLayer(packageName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun EventLogAssertionBuilderLegacy.focusChanges(
     vararg windows: String,
     bugId: Int = 0,
@@ -320,6 +584,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index b5fd4a5..c507841 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -50,7 +49,6 @@
  * Test app closes by pressing back button
  * To run this test: `atest FlickerTests:CloseAppBackButtonTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("closeAppBackButton", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -89,29 +87,47 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            launcherReplacesAppWindowAsTopWindow(testApp)
-                            wallpaperWindowBecomesVisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                launcherReplacesAppWindowAsTopWindow(testApp)
+                                wallpaperWindowBecomesVisible()
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                wallpaperLayerReplacesAppLayer(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                            }
 
-                            wallpaperLayerReplacesAppLayer(testApp)
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 584e4b1..d1c3efe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -37,6 +37,7 @@
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
@@ -64,7 +65,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("closeAppHomeButton", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,28 +89,46 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            launcherReplacesAppWindowAsTopWindow(testApp)
-                            wallpaperWindowBecomesVisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                launcherReplacesAppWindowAsTopWindow(testApp)
+                                wallpaperWindowBecomesVisible()
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                wallpaperLayerReplacesAppLayer(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            val isRotation0 = configuration.startRotation == Surface.ROTATION_0
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0, enabled = isRotation0)
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0, enabled = isRotation0)
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                            }
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
 
-                            wallpaperLayerReplacesAppLayer(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 1a47449..323236e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -43,7 +43,7 @@
         wmHelper.waitForAppTransitionIdle()
 
         // Ensure WindowManagerService wait until all animations have completed
-        instrumentation.getUiAutomation().syncInputTransactions()
+        instrumentation.uiAutomation.syncInputTransactions()
     } catch (e: RemoteException) {
         throw RuntimeException(e)
     }
@@ -71,8 +71,8 @@
 /**
  * Build a test tag for the test
  * @param testName Name of the transition(s) being tested
- * @param app App being launcher
  * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
  *
  * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
 </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -92,9 +92,30 @@
 
 /**
  * Build a test tag for the test
+ * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+@JvmOverloads
+fun buildTestTag(
+    configuration: Bundle,
+    extraInfo: String = ""
+): String {
+    return buildTestTag(testName = null,
+        app = null,
+        beginRotation = configuration.startRotation,
+        endRotation = configuration.endRotation,
+        app2 = null,
+        extraInfo = extraInfo)
+}
+
+/**
+ * Build a test tag for the test
  * @param testName Name of the transition(s) being tested
  * @param app App being launcher
  * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
  *
  * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
 </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -121,14 +142,17 @@
  * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
 </EXTRA></NAME> */
 fun buildTestTag(
-    testName: String,
+    testName: String?,
     app: String?,
     beginRotation: Int,
     endRotation: Int,
     app2: String?,
     extraInfo: String
 ): String {
-    var testTag = testName
+    var testTag = ""
+    if (testName != null) {
+        testTag += testName
+    }
     if (app != null) {
         testTag += "__$app"
     }
@@ -142,5 +166,9 @@
     if (extraInfo.isNotEmpty()) {
         testTag += "__$extraInfo"
     }
+
+    if (testTag.startsWith("__")) {
+        testTag = testTag.drop(2)
+    }
     return testTag
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index fde97ba..c7736f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -48,11 +46,9 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeAutoOpenWindowToAppTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
                     val testApp = ImeAppAutoFocusHelper(instrumentation,
                         configuration.startRotation)
-                    withTestName { buildTestTag("imeToAppAutoOpen", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -89,26 +85,39 @@
                         testApp.closeIME(device, wmHelper)
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeAppWindowIsAlwaysVisible(testApp)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeAppWindowIsAlwaysVisible(testApp)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                imeLayerBecomesInvisible()
+                                imeAppLayerIsAlwaysVisible(testApp)
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            imeLayerBecomesInvisible()
-                            imeAppLayerIsAlwaysVisible(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
                     }
                 }
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 ab7c08f..aa24456 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
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,7 +46,6 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
                     val testApp = ImeAppAutoFocusHelper(instrumentation,
                         configuration.startRotation)
                     withTestName {
-                        buildTestTag("imeToHomeAutoOpen", configuration)
+                        buildTestTag(configuration)
                     }
                     repeat { configuration.repetitions }
                     setup {
@@ -90,32 +88,50 @@
                         wmHelper.waitImeWindowGone()
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesInvisible()
-                            imeAppWindowBecomesInvisible(testApp)
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+
+                                imeWindowBecomesInvisible()
+                                imeAppWindowBecomesInvisible(testApp)
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+                                imeLayerBecomesInvisible()
+                                imeAppLayerBecomesInvisible(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    navBarLayerIsAlwaysVisible()
+                                    statusBarLayerIsAlwaysVisible()
+                                    visibleLayersShownMoreThanOneConsecutiveEntry(
+                                        listOf(IME_WINDOW_TITLE))
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible(
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerIsAlwaysVisible(
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
-                                enabled = !configuration.startRotation.isRotated())
-
-                            imeLayerBecomesInvisible()
-                            imeAppLayerBecomesInvisible(testApp)
+                        flaky {
+                            layersTrace {
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    navBarLayerIsAlwaysVisible()
+                                    statusBarLayerIsAlwaysVisible()
+                                    visibleLayersShownMoreThanOneConsecutiveEntry(
+                                        listOf(IME_WINDOW_TITLE))
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0503cce0..2bd5abb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeWindowToAppTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -64,7 +60,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("imeToApp", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -86,24 +82,25 @@
                         testApp.closeIME(device, wmHelper)
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeAppWindowIsAlwaysVisible(testApp)
+                            }
 
-                            imeAppWindowIsAlwaysVisible(testApp)
-                        }
-
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation)
-                            statusBarLayerRotatesScales(configuration.startRotation)
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
-
-                            imeLayerBecomesInvisible()
-                            imeAppLayerIsAlwaysVisible(testApp)
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                navBarLayerRotatesAndScales(configuration.startRotation)
+                                statusBarLayerRotatesScales(configuration.startRotation)
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+                                imeLayerBecomesInvisible()
+                                imeAppLayerIsAlwaysVisible(testApp)
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 9cb075b..7b61bb5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
  * Test IME window closing to home transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeWindowToHomeTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -63,7 +59,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("imeToHome", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -91,31 +87,47 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesInvisible()
-                            imeAppWindowBecomesInvisible(testApp)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeWindowBecomesInvisible()
+                                imeAppWindowBecomesInvisible(testApp)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                imeLayerBecomesInvisible()
+                                imeAppLayerBecomesInvisible(testApp)
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
-                                enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
 
-                            imeLayerBecomesInvisible()
-                            imeAppLayerBecomesInvisible(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index c775cb8..cfdd856 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,106 +17,82 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 
 const val IME_WINDOW_TITLE = "InputMethod"
 
 @JvmOverloads
-fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeLayerBecomesVisible", bugId, enabled) {
+fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) {
+    all("imeLayerBecomesVisible", bugId) {
         this.hidesLayer(IME_WINDOW_TITLE)
                 .then()
                 .showsLayer(IME_WINDOW_TITLE)
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) {
+    all("imeLayerBecomesInvisible", bugId) {
         this.showsLayer(IME_WINDOW_TITLE)
                 .then()
                 .hidesLayer(IME_WINDOW_TITLE)
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppLayerIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppLayerIsAlwaysVisible", bugId) {
         this.showsLayer(testApp.getPackage())
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppWindowIsAlwaysVisible", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
     }
 }
 
-fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) {
+    all("imeWindowBecomesVisible", bugId) {
         this.hidesNonAppWindow(IME_WINDOW_TITLE)
                 .then()
                 .showsNonAppWindow(IME_WINDOW_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) {
+    all("imeWindowBecomesInvisible", bugId) {
         this.showsNonAppWindow(IME_WINDOW_TITLE)
                 .then()
                 .hidesNonAppWindow(IME_WINDOW_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
-    windowName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) {
+    all("imeAppWindowBecomesVisible", bugId) {
         this.hidesAppWindow(windowName)
                 .then()
                 .showsAppWindow(windowName)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppWindowBecomesInvisible", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
                 .then()
                 .appWindowNotOnTop(testApp.getPackage())
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppLayerBecomesInvisible", bugId) {
         this.skipUntilFirstAssertion()
                 .showsLayer(testApp.getPackage())
                 .then()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 13c6cd7..9e94d6e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -50,11 +48,9 @@
  * Test IME window opening transitions.
  * To run this test: `atest FlickerTests:OpenImeWindowTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class OpenImeWindowTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("openIme", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,27 +84,43 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesVisible()
-                            appWindowAlwaysVisibleOnTop(testApp.`package`)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+
+                                imeWindowBecomesVisible()
+                                appWindowAlwaysVisibleOnTop(testApp.`package`)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                imeLayerBecomesVisible()
+                                layerAlwaysVisible(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            }
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            imeLayerBecomesVisible()
-                            layerAlwaysVisible(testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
                     }
                 }
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 6cc64df..2fe49af 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
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
  * Test IME window opening transitions.
  * To run this test: `atest FlickerTests:ReOpenImeWindowTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,34 +65,37 @@
             val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 1) { configuration ->
-                        val testApp = ImeAppAutoFocusHelper(instrumentation,
-                            configuration.startRotation)
-                        withTestName { buildTestTag("reOpenImeAutoFocus", configuration) }
-                        repeat { configuration.repetitions }
-                        setup {
-                            test {
-                                device.wakeUpAndGoToHomeScreen()
-                                testApp.launchViaIntent(wmHelper)
-                                testApp.openIME(device, wmHelper)
-                            }
-                            eachRun {
-                                device.pressRecentApps()
-                                wmHelper.waitImeWindowGone()
-                                wmHelper.waitForAppTransitionIdle()
-                                this.setRotation(configuration.startRotation)
-                            }
+                    val testApp = ImeAppAutoFocusHelper(instrumentation,
+                        configuration.startRotation)
+                    withTestName { buildTestTag(configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.launchViaIntent(wmHelper)
+                            testApp.openIME(device, wmHelper)
                         }
-                        transitions {
-                            device.reopenAppFromOverview()
-                            wmHelper.waitImeWindowShown()
+                        eachRun {
+                            device.pressRecentApps()
+                            wmHelper.waitImeWindowGone()
+                            wmHelper.waitForAppTransitionIdle()
+                            this.setRotation(configuration.startRotation)
                         }
-                        teardown {
-                            test {
-                                this.setRotation(Surface.ROTATION_0)
-                                testApp.exit()
-                            }
+                    }
+                    transitions {
+                        device.reopenAppFromOverview()
+                        wmHelper.waitImeWindowShown()
+                    }
+                    teardown {
+                        test {
+                            this.setRotation(Surface.ROTATION_0)
+                            testApp.exit()
                         }
-                        assertions {
+                    }
+                    assertions {
+                        val isRotated = configuration.startRotation.isRotated()
+
+                        presubmit {
                             windowManagerTrace {
                                 navBarWindowIsAlwaysVisible()
                                 statusBarWindowIsAlwaysVisible()
@@ -107,21 +108,34 @@
 
                             layersTrace {
                                 noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                        configuration.endRotation,
-                                        enabled = !configuration.startRotation.isRotated())
-                                statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                        configuration.endRotation,
-                                        enabled = !configuration.startRotation.isRotated())
                                 statusBarLayerIsAlwaysVisible()
                                 navBarLayerIsAlwaysVisible()
-                                visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
-
                                 imeLayerBecomesVisible()
                                 appLayerReplacesWallpaperLayer(testAppComponentName.className)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+                        }
+
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
                             }
                         }
                     }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 1bd1190..be3fa5f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,14 +17,10 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 
-fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowReplacesLauncherAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+    all("appWindowReplacesLauncherAsTopWindow", bugId) {
         this.showsAppWindowOnTop("Launcher")
                 .then()
                 .showsAppWindowOnTop("Snapshot", testApp.getPackage())
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 010ebf2..0ec0b04 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,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest FlickerTests:OpenAppColdTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -66,7 +64,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation) { configuration ->
-                    withTestName { buildTestTag("openAppCold", testApp, configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,33 +86,48 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                // During testing the launcher is always in portrait mode
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            // During testing the launcher is always in portrait mode
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
-                        }
-
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
                         }
                     }
                 }
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 5e08921..84cc8e3 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
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
  * Launch an app from the recents app view (the overview)
  * To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,7 +65,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("openAppFromOverview", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -92,36 +90,57 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                } else {
+                                    statusBarLayerIsAlwaysVisible()
+                                    navBarLayerIsAlwaysVisible()
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
-                                bugId = 141361128)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerIsAlwaysVisible(
-                                enabled = Surface.ROTATION_0 == configuration.endRotation)
-                            navBarLayerIsAlwaysVisible(
-                                enabled = Surface.ROTATION_0 == configuration.endRotation)
-                            visibleLayersShownMoreThanOneConsecutiveEntry(
-                                enabled = false)
-
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
+                        postsubmit {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            }
                         }
 
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                    bugId = 141361128)
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                } else {
+                                    statusBarLayerIsAlwaysVisible()
+                                    navBarLayerIsAlwaysVisible()
+                                }
+                            }
                         }
                     }
                 }
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 092cd4d..1f375a5 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,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
  * Test warm launch app.
  * To run this test: `atest FlickerTests:OpenAppWarmTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -64,7 +62,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation) { configuration ->
-                    withTestName { buildTestTag("openAppWarm", testApp, configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -91,33 +89,49 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                // During testing the launcher is always in portrait mode
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            // During testing the launcher is always in portrait mode
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
-                        }
-
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 1c44b21..7bfdd96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -48,7 +47,6 @@
  * Cycle through supported app rotations.
  * To run this test: `atest FlickerTests:ChangeAppRotationTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,51 +65,56 @@
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
-                withTestName { buildTestTag("changeAppRotation", configuration) }
+                withTestName { buildTestTag(configuration) }
                 assertions {
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        visibleWindowsShownMoreThanOneConsecutiveEntry()
-                    }
-
-                    layersTrace {
-                        navBarLayerIsAlwaysVisible(bugId = 140855415)
-                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                        noUncoveredRegions(configuration.startRotation,
-                            configuration.endRotation, allStates = false)
-                        navBarLayerRotatesAndScales(configuration.startRotation,
-                            configuration.endRotation, bugId = 140855415)
-                        statusBarLayerRotatesScales(configuration.startRotation,
-                            configuration.endRotation, bugId = 140855415)
-                        visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
-                    }
-
-                    layersTrace {
-                        val startingPos = WindowUtils.getDisplayBounds(
-                            configuration.startRotation)
-                        val endingPos = WindowUtils.getDisplayBounds(
-                            configuration.endRotation)
-
-                        start("appLayerRotates_StartingPos", bugId = 140855415) {
-                            this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                    presubmit {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            visibleWindowsShownMoreThanOneConsecutiveEntry()
                         }
 
-                        end("appLayerRotates_EndingPos", bugId = 140855415) {
-                            this.hasVisibleRegion(testApp.getPackage(), endingPos)
-                        }
+                        layersTrace {
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false)
 
-                        all("screenshotLayerBecomesInvisible") {
-                            this.showsLayer(testApp.getPackage())
-                                .then()
-                                .showsLayer(SCREENSHOT_LAYER)
-                                .then()
-                                .showsLayer(testApp.getPackage())
+                            all("screenshotLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                    .then()
+                                    .showsLayer(SCREENSHOT_LAYER)
+                                    .then()
+                                    .showsLayer(testApp.getPackage())
+                            }
                         }
                     }
 
-                    eventLog {
-                        focusDoesNotChange(bugId = 151179149)
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
+
+                            val startingPos = WindowUtils.getDisplayBounds(
+                                configuration.startRotation)
+                            val endingPos = WindowUtils.getDisplayBounds(
+                                configuration.endRotation)
+
+                            start("appLayerRotates_StartingPos", bugId = 140855415) {
+                                this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                            }
+
+                            end("appLayerRotates_EndingPos", bugId = 140855415) {
+                                this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
             }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index f04131b..7861464 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -51,7 +50,6 @@
  * Cycle through supported app rotations using seamless rotations.
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -97,63 +95,67 @@
                     } else {
                         ""
                     }
-                    buildTestTag("seamlessRotation", configuration, extraInfo = extra)
+                    buildTestTag(configuration, extraInfo = extra)
                 }
                 assertions {
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible(bugId = 140855415)
-                        statusBarWindowIsAlwaysVisible(bugId = 140855415)
-                        visibleWindowsShownMoreThanOneConsecutiveEntry()
-                        appWindowAlwaysVisibleOnTop(testApp.`package`)
-                    }
+                    val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+                    val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
 
-                    layersTrace {
-                        navBarLayerIsAlwaysVisible(bugId = 140855415)
-                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                        noUncoveredRegions(configuration.startRotation,
-                            configuration.endRotation, allStates = false, bugId = 147659548)
-                        navBarLayerRotatesAndScales(configuration.startRotation,
-                            configuration.endRotation,
-                            enabled = false)
-                        statusBarLayerRotatesScales(configuration.startRotation,
-                            configuration.endRotation, enabled = false)
-                        visibleLayersShownMoreThanOneConsecutiveEntry(
-                                enabled = configuration.startRotation == configuration.endRotation)
-                        layerAlwaysVisible(testApp.`package`)
-                    }
-
-                    layersTrace {
-                        val startingBounds = WindowUtils
-                            .getDisplayBounds(configuration.startRotation)
-                        val endingBounds = WindowUtils
-                            .getDisplayBounds(configuration.endRotation)
-
-                        all("appLayerRotates", bugId = 147659548) {
-                            if (startingBounds == endingBounds) {
-                                this.hasVisibleRegion(
-                                    testApp.`package`, startingBounds)
-                            } else {
-                                this.hasVisibleRegion(testApp.`package`,
-                                    startingBounds)
-                                    .then()
-                                    .hasVisibleRegion(testApp.`package`,
-                                        endingBounds)
-                            }
+                    presubmit {
+                        windowManagerTrace {
+                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            appWindowAlwaysVisibleOnTop(testApp.`package`)
                         }
 
-                        all("noUncoveredRegions", bugId = 147659548) {
-                            if (startingBounds == endingBounds) {
-                                this.coversAtLeastRegion(startingBounds)
-                            } else {
-                                this.coversAtLeastRegion(startingBounds)
-                                    .then()
-                                    .coversAtLeastRegion(endingBounds)
-                            }
+                        layersTrace {
+                            layerAlwaysVisible(testApp.`package`)
                         }
                     }
 
-                    eventLog {
-                        focusDoesNotChange(bugId = 151179149)
+                    flaky {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible(bugId = 140855415)
+                            statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false, bugId = 147659548)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation)
+                            visibleLayersShownMoreThanOneConsecutiveEntry()
+
+                            all("appLayerRotates", bugId = 147659548) {
+                                if (startingBounds == endingBounds) {
+                                    this.hasVisibleRegion(
+                                        testApp.`package`, startingBounds)
+                                } else {
+                                    this.hasVisibleRegion(testApp.`package`,
+                                        startingBounds)
+                                        .then()
+                                        .hasVisibleRegion(testApp.`package`,
+                                            endingBounds)
+                                }
+                            }
+
+                            all("noUncoveredRegions", bugId = 147659548) {
+                                if (startingBounds == endingBounds) {
+                                    this.coversAtLeastRegion(startingBounds)
+                                } else {
+                                    this.coversAtLeastRegion(startingBounds)
+                                        .then()
+                                        .coversAtLeastRegion(endingBounds)
+                                }
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
             }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a940c7..c6c67fe 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -753,6 +753,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="RenderEffectShaderActivity"
+                  android:label="RenderEffect/Shader"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="TextActivity"
              android:label="Text/Simple Text"
              android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
new file mode 100644
index 0000000..661d48a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RenderEffectShaderActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout layout = new LinearLayout(this);
+        layout.setClipChildren(false);
+        layout.setGravity(Gravity.CENTER);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+        params.bottomMargin = 100;
+
+        layout.addView(new ShaderRenderEffectView(this), params);
+
+        setContentView(layout);
+    }
+
+    public static class ShaderRenderEffectView extends View {
+
+        private final Paint mPaint;
+        private final RenderNode mRenderNode;
+
+        public ShaderRenderEffectView(Context c) {
+            super(c);
+
+            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mRenderNode = new RenderNode("blurNode");
+
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            if (changed) {
+                LinearGradient gradient = new LinearGradient(
+                        0f, 0f,
+                        0f, bottom - top,
+                        new int[]{Color.CYAN, Color.MAGENTA},
+                        null,
+                        Shader.TileMode.CLAMP
+                );
+                mRenderNode.setRenderEffect(
+                        RenderEffect.createShaderEffect(gradient)
+                );
+
+                int width = right - left;
+                int height = bottom - top;
+                mRenderNode.setPosition(0, 0, width, height);
+                Canvas canvas = mRenderNode.beginRecording(width, height);
+                mPaint.setColor(Color.BLUE);
+
+                canvas.drawRect(
+                        0,
+                        0,
+                        width,
+                        height,
+                        mPaint
+                );
+
+                mPaint.setColor(Color.RED);
+                canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+                mRenderNode.endRecording();
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRenderNode(mRenderNode);
+        }
+    }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 9b15b04..5c26448 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -34,12 +34,14 @@
     constructor(context: Context) : this(context, null)
     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
         displayManager = context.getSystemService(DisplayManager::class.java)!!
+        displayId = context.getDisplayId()
     }
 
     private var window: Window? = null
     private var currentModeDisplay: TextView? = null
     private val displayManager: DisplayManager
     private var targetSdrWhitePointIndex = 0
+    private var displayId: Int
 
     private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
 
@@ -107,7 +109,7 @@
         // Imperfect, but close enough, synchronization by waiting for frame commit to set the value
         viewTreeObserver.registerFrameCommitCallback {
             try {
-                displayManager.setTemporaryBrightness(level)
+                displayManager.setTemporaryBrightness(displayId, level)
             } catch (ex: Exception) {
                 // Ignore a permission denied rejection - it doesn't meaningfully change much
             }
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index d809fe8..43a5078 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -16,8 +16,14 @@
     name: "UpdatableSystemFontTest",
     srcs: ["src/**/*.java"],
     libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
-    static_libs: ["frameworks-base-hostutils"],
+    static_libs: [
+        "block_device_writer_jar",
+        "frameworks-base-hostutils",
+    ],
     test_suites: ["general-tests", "vts"],
+    target_required: [
+        "block_device_writer_module",
+    ],
     data: [
         ":NotoColorEmojiTtf",
         ":UpdatableSystemFontTestCertDer",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index efe5d70..7b919bd 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -21,6 +21,7 @@
 
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
+        <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
         <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
         <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
         <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6d161a5..e249f8a9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -21,7 +21,9 @@
 
 import android.platform.test.annotations.RootPermissionTest;
 
+import com.android.blockdevicewriter.BlockDeviceWriter;
 import com.android.fsverity.AddFsVerityCertRule;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -34,6 +36,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -126,6 +130,44 @@
                 TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
     }
 
+    @Test
+    public void reboot() throws Exception {
+        expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+                TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+        String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+        assertThat(fontPath).startsWith("/data/fonts/files/");
+
+        expectRemoteCommandToSucceed("stop");
+        expectRemoteCommandToSucceed("start");
+        waitUntilFontCommandIsReady();
+        String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+        assertThat(fontPathAfterReboot).isEqualTo(fontPath);
+    }
+
+    @Test
+    public void reboot_clearDamagedFiles() throws Exception {
+        expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+                TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+        String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+        assertThat(fontPath).startsWith("/data/fonts/files/");
+        assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isTrue();
+
+        BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), fontPath, 0);
+        expectRemoteCommandToSucceed("stop");
+        // We have to make sure system_server is gone before dropping caches, because system_server
+        // process holds font memory maps and prevents cache eviction.
+        waitUntilSystemServerIsGone();
+        BlockDeviceWriter.assertFileNotOpen(getDevice(), fontPath);
+        BlockDeviceWriter.dropCaches(getDevice());
+        assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isFalse();
+
+        expectRemoteCommandToSucceed("start");
+        waitUntilFontCommandIsReady();
+        String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+        assertWithMessage("Damaged file should be deleted")
+                .that(fontPathAfterReboot).startsWith("/system");
+    }
+
     private String getFontPath(String fontFileName) throws Exception {
         // TODO: add a dedicated command for testing.
         String lines = expectRemoteCommandToSucceed("cmd font dump");
@@ -153,4 +195,39 @@
                 .that(result.getStatus())
                 .isNotEqualTo(CommandStatus.SUCCESS);
     }
+
+    private void waitUntilFontCommandIsReady() {
+        waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+            try {
+                return getDevice().executeShellV2Command("cmd font status").getStatus()
+                        == CommandStatus.SUCCESS;
+            } catch (DeviceNotAvailableException e) {
+                return false;
+            }
+        });
+    }
+
+    private void waitUntilSystemServerIsGone() {
+        waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+            try {
+                return getDevice().executeShellV2Command("pid system_server").getStatus()
+                        == CommandStatus.FAILED;
+            } catch (DeviceNotAvailableException e) {
+                return false;
+            }
+        });
+    }
+
+    private void waitUntil(long timeoutMillis, Supplier<Boolean> func) {
+        long untilMillis = System.currentTimeMillis() + timeoutMillis;
+        do {
+            if (func.get()) return;
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                throw new AssertionError("Interrupted", e);
+            }
+        } while (System.currentTimeMillis() < untilMillis);
+        throw new AssertionError("Timed out");
+    }
 }
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba..d232a507 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -20,22 +20,20 @@
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
-import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
 
 @IgnoreUpTo(Build.VERSION_CODES.R)
 @RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@
     private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
     private static final String TEST_PACKAGE = "com.google.apps.contacts";
 
-    private final List<String> mPackages = new ArrayList<>();
     private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
 
-    @Before
-    public void beforeEachTestMethod() {
-        mPackages.add(TEST_PACKAGE);
-    }
-
     @Test
-    public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+    public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
         assertThrows(NullPointerException.class,
-                () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+                () -> mBuilder.addNetworkPreference(null, TEST_PREF));
     }
 
     @Test
-    public void getNetworkPreferencesReturnsCorrectValue() {
-        final int expectedNumberOfMappings = 1;
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+    public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
+        assertThrows(NullPointerException.class,
+                () -> mBuilder.removeNetworkPreference(null));
+    }
 
-        final SparseArray<List<String>> networkPreferences =
+    @Test
+    public void testGetNetworkPreferenceReturnsCorrectValue() {
+        final int expectedNumberOfMappings = 1;
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+
+        final Map<String, Integer> networkPreferences =
                 mBuilder.build().getNetworkPreferences();
 
         assertEquals(expectedNumberOfMappings, networkPreferences.size());
-        assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
-        assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
     }
 
     @Test
-    public void getNetworkPreferencesReturnsUnmodifiableValue() {
+    public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
         final String newPackage = "new.com.google.apps.contacts";
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
-        final SparseArray<List<String>> networkPreferences =
+        final Map<String, Integer> networkPreferences =
                 mBuilder.build().getNetworkPreferences();
 
         assertThrows(UnsupportedOperationException.class,
-                () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+                () -> networkPreferences.put(newPackage, TEST_PREF));
 
         assertThrows(UnsupportedOperationException.class,
-                () -> networkPreferences.get(TEST_PREF).add(newPackage));
+                () -> networkPreferences.remove(TEST_PACKAGE));
+
     }
 
     @Test
-    public void toStringReturnsCorrectValue() {
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+    public void testToStringReturnsCorrectValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
         final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
 
@@ -99,10 +97,56 @@
 
     @Test
     public void testOemNetworkPreferencesParcelable() {
-        mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
 
         final OemNetworkPreferences prefs = mBuilder.build();
 
         assertParcelSane(prefs, 1 /* fieldCount */);
     }
+
+    @Test
+    public void testAddNetworkPreferenceOverwritesPriorPreference() {
+        final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        Map<String, Integer> networkPreferences =
+                mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+        mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+        networkPreferences = mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+    }
+
+    @Test
+    public void testRemoveNetworkPreferenceRemovesValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        Map<String, Integer> networkPreferences =
+                mBuilder.build().getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+        mBuilder.removeNetworkPreference(TEST_PACKAGE);
+        networkPreferences = mBuilder.build().getNetworkPreferences();
+
+        assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+    }
+
+    @Test
+    public void testConstructorByOemNetworkPreferencesSetsValue() {
+        mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+        OemNetworkPreferences networkPreference = mBuilder.build();
+
+        final Map<String, Integer> networkPreferences =
+                new OemNetworkPreferences
+                        .Builder(networkPreference)
+                        .build()
+                        .getNetworkPreferences();
+
+        assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+        assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+    }
 }
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587..e1da3d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index c2fddf3..6a09b02 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -35,6 +35,7 @@
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.REQUEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
+import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -329,6 +330,9 @@
         mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); });
         mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); });
 
+        mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); });
+        mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); });
+
         mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); });
         mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); });
         mustFail(() -> { manager.releaseNetworkRequest(nullIntent); });
@@ -345,15 +349,17 @@
     @Test
     public void testRequestType() throws Exception {
         final String testPkgName = "MyPackage";
+        final String testAttributionTag = "MyTag";
         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
         when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+        when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
         final NetworkRequest request = makeRequest(1);
         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
@@ -361,19 +367,26 @@
         verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
                 anyInt(), any(), any());
         verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
-                eq(testPkgName));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.requestBackgroundNetwork(request, null, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
+        Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+        manager.registerSystemDefaultNetworkCallback(callback, handler);
+        verify(mService).requestNetwork(eq(null),
+                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
 
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 91fcbc0..1f8f6f3 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -35,7 +35,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 import kotlin.test.assertFalse
@@ -60,16 +59,13 @@
         subscriberId: String? = null,
         ssid: String? = null
     ): NetworkState {
-        val info = mock(NetworkInfo::class.java)
-        doReturn(type).`when`(info).type
-        doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
         val lp = LinkProperties()
         val caps = NetworkCapabilities().apply {
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
             setSSID(ssid)
         }
-        return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+        return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid)
     }
 
     private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
new file mode 100644
index 0000000..2fd5e38
--- /dev/null
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VpnTransportInfoTest {
+
+    @Test
+    public void testParceling() {
+        VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+        assertParcelSane(v, 1 /* fieldCount */);
+        assertParcelingIsLossless(v);
+    }
+
+    @Test
+    public void testEqualsAndHashCode() {
+        VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+        VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
+        VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+        assertNotEquals(v1, v2);
+        assertEquals(v1, v3);
+        assertEquals(v1.hashCode(), v3.hashCode());
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0674138..e1fd8f0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -205,6 +205,7 @@
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
 import android.net.VpnManager;
+import android.net.VpnTransportInfo;
 import android.net.metrics.IpConnectivityLog;
 import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
@@ -331,11 +332,12 @@
     private static final String TAG = "ConnectivityServiceTest";
 
     private static final int TIMEOUT_MS = 500;
-    private static final int TEST_LINGER_DELAY_MS = 300;
-    // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
-    // LOST callback that arrives immediately and a LOST callback that arrives after the linger
-    // timeout. For this, our assertions should run fast enough to leave less than
-    // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+    private static final int TEST_LINGER_DELAY_MS = 400;
+    private static final int TEST_NASCENT_DELAY_MS = 300;
+    // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+    // between a LOST callback that arrives immediately and a LOST callback that arrives after
+    // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+    // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
     // supposedly fired, and the time we call expectCallback.
     private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
     // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -1109,7 +1111,7 @@
         }
 
         @Override
-        public int getActiveAppVpnType() {
+        public int getActiveVpnType() {
             return mVpnType;
         }
 
@@ -1122,10 +1124,12 @@
         private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
                 throws Exception {
             if (mAgentRegistered) throw new IllegalStateException("already registered");
+            updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
             mConfig = new VpnConfig();
             setUids(uids);
             if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
             mInterface = VPN_IFNAME;
+            mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
             mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -1262,6 +1266,13 @@
         }
     }
 
+    private void processBroadcastForVpn(Intent intent) {
+        // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
+        final Handler handler = new Handler(mCsHandlerThread.getLooper());
+        handler.post(() -> mServiceContext.sendBroadcast(intent));
+        waitForIdle();
+    }
+
     private void mockUidNetworkingBlocked() {
         doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
                 .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
@@ -1395,6 +1406,7 @@
                 mMockNetd,
                 mDeps);
         mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+        mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
         verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1737,6 +1749,108 @@
         verifyNoNetwork();
     }
 
+    /**
+     * Verify a newly created network will be inactive instead of torn down even if no one is
+     * requesting.
+     */
+    @Test
+    public void testNewNetworkInactive() throws Exception {
+        // Create a callback that monitoring the testing network.
+        final TestNetworkCallback listenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+        // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+        // default requests. Verify that the network will be inactive instead of torn down.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        listenCallback.assertNoCallback();
+
+        // Verify that the network will be torn down after nascent expiry. A small period of time
+        // is added in case of flakiness.
+        final int nascentTimeoutMs =
+                mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+        // 2. Create a network that is satisfied by a request comes later.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+        mCm.requestNetwork(wifiRequest, wifiCallback);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Verify that the network will be kept since the request is still satisfied. And is able
+        // to get disconnected as usual if the request is released after the nascent timer expires.
+        listenCallback.assertNoCallback(nascentTimeoutMs);
+        mCm.unregisterNetworkCallback(wifiCallback);
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+        // 3. Create a network that is satisfied by a request comes later.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithoutInternet();
+        listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        mCm.requestNetwork(wifiRequest, wifiCallback);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Verify that the network will still be torn down after the request gets removed.
+        mCm.unregisterNetworkCallback(wifiCallback);
+        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+        // There is no need to ensure that LOSING is never sent in the common case that the
+        // network immediately satisfies a request that was already present, because it is already
+        // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+        mCm.unregisterNetworkCallback(listenCallback);
+    }
+
+    /**
+     * Verify a newly created network will be inactive and switch to background if only background
+     * request is satisfied.
+     */
+    @Test
+    public void testNewNetworkInactive_bgNetwork() throws Exception {
+        // Create a callback that monitoring the wifi network.
+        final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+        // Create callbacks that can monitor background and foreground mobile networks.
+        // This is done by granting using background networks permission before registration. Thus,
+        // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+        grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+        final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+        final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+        mCm.registerNetworkCallback(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+        // Connect wifi, which satisfies default request.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Connect a cellular network, verify that satisfies only the background callback.
+        setAlwaysOnNetworks(true);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        fgMobileListenCallback.assertNoCallback();
+        assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+        mCellNetworkAgent.disconnect();
+        bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        fgMobileListenCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(wifiListenCallback);
+        mCm.unregisterNetworkCallback(bgMobileListenCallback);
+        mCm.unregisterNetworkCallback(fgMobileListenCallback);
+    }
+
     @Test
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
@@ -2596,6 +2710,10 @@
 
         NetworkCapabilities filter = new NetworkCapabilities();
         filter.addCapability(capability);
+        // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+        // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+        // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+        filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
         handlerThread.start();
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -3534,10 +3652,19 @@
 
     @Test
     public void testRegisterDefaultNetworkCallback() throws Exception {
+        // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+                PERMISSION_GRANTED);
+
         final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
         defaultNetworkCallback.assertNoCallback();
 
+        final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+        final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
+        mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler);
+        systemDefaultCallback.assertNoCallback();
+
         // Create a TRANSPORT_CELLULAR request to keep the mobile interface up
         // whenever Wi-Fi is up. Without this, the mobile network agent is
         // reaped before any other activity can take place.
@@ -3552,27 +3679,35 @@
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring up wifi and expect CALLBACK_AVAILABLE.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         cellNetworkCallback.assertNoCallback();
         defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring down cell. Expect no default network callback, since it wasn't the default.
         mCellNetworkAgent.disconnect();
         cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
+        systemDefaultCallback.assertNoCallback();
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring up cell. Expect no default network callback, since it won't be the default.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
+        systemDefaultCallback.assertNoCallback();
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Bring down wifi. Expect the default network callback to notified of LOST wifi
         // followed by AVAILABLE cell.
@@ -3580,19 +3715,25 @@
         cellNetworkCallback.assertNoCallback();
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
         cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
 
         mMockVpn.establishForMyUid();
         assertUidRangesUpdatedForMyUid(true);
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        systemDefaultCallback.assertNoCallback();
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(null, systemDefaultCallback.getLastAvailableNetwork());
 
         mMockVpn.disconnect();
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        systemDefaultCallback.assertNoCallback();
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
     }
@@ -3847,6 +3988,7 @@
         handlerThread.start();
         NetworkCapabilities filter = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_INTERNET);
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                 mServiceContext, "testFactory", filter);
@@ -3894,8 +4036,9 @@
             setAlwaysOnNetworks(false);
             testFactory.expectRequestRemove();
 
-            // ...  and cell data to be torn down.
-            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            // ...  and cell data to be torn down after nascent network timeout.
+            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+                    mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
             assertLength(1, mCm.getAllNetworks());
         } finally {
             testFactory.terminate();
@@ -5300,20 +5443,20 @@
         // MOBILE_IFNAME even though the default network is wifi.
         // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
         // applies to the system server UID should not have any bearing on network stats.
-        mService.setUnderlyingNetworksForVpn(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME});
         reset(mStatsService);
 
-        mService.setUnderlyingNetworksForVpn(cellAndWifi);
+        mMockVpn.setUnderlyingNetworks(cellAndWifi);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
         reset(mStatsService);
 
         // Null underlying networks are ignored.
-        mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+        mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5362,25 +5505,25 @@
         // is probably a performance improvement (though it's very unlikely that a VPN would declare
         // no underlying networks).
         // Also, for the same reason as above, the active interface passed in is null.
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Specifying only a null underlying network is the same as no networks.
-        mService.setUnderlyingNetworksForVpn(onlyNull);
+        mMockVpn.setUnderlyingNetworks(onlyNull);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Specifying networks that are all disconnected is the same as specifying no networks.
-        mService.setUnderlyingNetworksForVpn(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, null);
         reset(mStatsService);
 
         // Passing in null again means follow the default network again.
-        mService.setUnderlyingNetworksForVpn(null);
+        mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
         expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{WIFI_IFNAME});
@@ -5749,6 +5892,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkDownstreamBandwidthKbps(10);
         final NetworkCapabilities wifiNc = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
@@ -5757,6 +5901,7 @@
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkUpstreamBandwidthKbps(20);
         mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
         mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -5855,7 +6000,7 @@
         mMockVpn.establishForMyUid(false, true, false);
         assertUidRangesUpdatedForMyUid(true);
         final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
-        mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+        mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
         callback.expectAvailableCallbacksUnvalidated(mMockVpn);
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
                 .hasTransport(TRANSPORT_VPN));
@@ -6015,6 +6160,10 @@
 
     @Test
     public void testVpnNetworkActive() throws Exception {
+        // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+                PERMISSION_GRANTED);
+
         final int uid = Process.myUid();
 
         final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -6022,6 +6171,7 @@
         final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
         final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
         final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build();
         final NetworkRequest genericRequest = new NetworkRequest.Builder()
                 .removeCapability(NET_CAPABILITY_NOT_VPN).build();
@@ -6035,6 +6185,8 @@
         mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
         mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
         mCm.registerDefaultNetworkCallback(defaultCallback);
+        mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+                new Handler(ConnectivityThread.getInstanceLooper()));
         defaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -6044,12 +6196,13 @@
         genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         vpnNetworkCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         final Set<UidRange> ranges = uidRangesForUid(uid);
         mMockVpn.registerAgent(ranges);
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
 
         // VPN networks do not satisfy the default request and are automatically validated
         // by NetworkMonitor
@@ -6064,7 +6217,10 @@
         wifiNetworkCallback.assertNoCallback();
         vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        systemDefaultCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+        assertEquals(mWiFiNetworkAgent.getNetwork(),
+                systemDefaultCallback.getLastAvailableNetwork());
 
         ranges.clear();
         mMockVpn.setUids(ranges);
@@ -6081,6 +6237,7 @@
         // much, but that is the reason the test here has to check for an update to the
         // capabilities instead of the expected LOST then AVAILABLE.
         defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+        systemDefaultCallback.assertNoCallback();
 
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
@@ -6092,6 +6249,7 @@
         // TODO : Here like above, AVAILABLE would be correct, but because this can't actually
         // happen outside of the test, ConnectivityService does not rematch callbacks.
         defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+        systemDefaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent.disconnect();
 
@@ -6100,6 +6258,7 @@
         wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         vpnNetworkCallback.assertNoCallback();
         defaultCallback.assertNoCallback();
+        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         mMockVpn.disconnect();
 
@@ -6108,12 +6267,14 @@
         wifiNetworkCallback.assertNoCallback();
         vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        systemDefaultCallback.assertNoCallback();
         assertEquals(null, mCm.getActiveNetwork());
 
         mCm.unregisterNetworkCallback(genericNetworkCallback);
         mCm.unregisterNetworkCallback(wifiNetworkCallback);
         mCm.unregisterNetworkCallback(vpnNetworkCallback);
         mCm.unregisterNetworkCallback(defaultCallback);
+        mCm.unregisterNetworkCallback(systemDefaultCallback);
     }
 
     @Test
@@ -6253,7 +6414,7 @@
 
     private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
         final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
-                userId, "com.android.calling.package");
+                userId, "com.android.calling.package", "com.test");
         final String defaultCapsString = Arrays.toString(defaultCaps);
         assertEquals(defaultCapsString, defaultCaps.length, networks.length);
         final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
@@ -6297,7 +6458,7 @@
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mCellNetworkAgent.connect(true);
 
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6312,7 +6473,7 @@
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mWiFiNetworkAgent.connect(true);
 
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6323,7 +6484,7 @@
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Don't disconnect, but note the VPN is not using wifi any more.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6354,7 +6515,7 @@
         vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
 
         // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6365,7 +6526,7 @@
         assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6380,7 +6541,7 @@
         vpnNetworkCallback.assertNoCallback();
 
         // Stop using WiFi. The VPN is suspended again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
@@ -6391,7 +6552,7 @@
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6526,9 +6687,7 @@
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
 
         // Send a USER_ADDED broadcast for it.
-        // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+        processBroadcastForVpn(addedIntent);
 
         // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
         // restricted user.
@@ -6552,7 +6711,7 @@
         // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+        processBroadcastForVpn(removedIntent);
 
         // Expect that the VPN gains the UID range for the restricted user, and that the capability
         // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6609,9 +6768,7 @@
         // TODO: check that VPN app within restricted profile still has access, etc.
         final Intent addedIntent = new Intent(ACTION_USER_ADDED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
-        waitForIdle();
+        processBroadcastForVpn(addedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -6621,8 +6778,7 @@
         // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
-        waitForIdle();
+        processBroadcastForVpn(removedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -6724,7 +6880,7 @@
         // Ensure VPN is now the active network.
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
         // VPN is using Cell
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6732,7 +6888,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is now using WiFi
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6740,7 +6896,7 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // VPN is using Cell | WiFi.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6748,7 +6904,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is using WiFi | Cell.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -6756,7 +6912,7 @@
         assertTrue(mCm.isActiveNetworkMetered());
 
         // VPN is not using any underlying networks.
-        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
 
         // VPN without underlying networks is treated as metered.
@@ -6783,7 +6939,7 @@
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
 
         // VPN is tracking current platform default (WiFi).
-        mService.setUnderlyingNetworksForVpn(null);
+        mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
 
         // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6791,7 +6947,7 @@
 
 
         // VPN explicitly declares WiFi as its underlying network.
-        mService.setUnderlyingNetworksForVpn(
+        mMockVpn.setUnderlyingNetworks(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
         waitForIdle();
 
@@ -7169,6 +7325,7 @@
     }
 
     private void establishLegacyLockdownVpn() throws Exception {
+        mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
         // The legacy lockdown VPN only supports userId 0.
         final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
         mMockVpn.registerAgent(ranges);
@@ -7199,9 +7356,7 @@
         final int userId = UserHandle.getUserId(Process.myUid());
         final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-        final Handler handler = new Handler(mCsHandlerThread.getLooper());
-        handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
-        waitForIdle();
+        processBroadcastForVpn(addedIntent);
 
         // Lockdown VPN disables teardown and enables lockdown.
         assertFalse(mMockVpn.getEnableTeardown());
@@ -7283,6 +7438,9 @@
         assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
         assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
+        VpnTransportInfo ti = (VpnTransportInfo) vpnNc.getTransportInfo();
+        assertNotNull(ti);
+        assertEquals(VpnManager.TYPE_VPN_LEGACY, ti.type);
 
         // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
         final LinkProperties wifiLp = new LinkProperties();
@@ -7409,19 +7567,13 @@
             mWiFiNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
             callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
-            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
-            //  it.
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-                reset(mMockNetd);
-            }
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
             mCellNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
             callbackWithoutCap.assertNoCallback();
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkClearDefault();
-            }
+            verify(mMockNetd).networkClearDefault();
 
             mCm.unregisterNetworkCallback(callbackWithCap);
             mCm.unregisterNetworkCallback(callbackWithoutCap);
@@ -8272,7 +8424,8 @@
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
         if (op != null) {
-            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
+                    eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         }
 
@@ -8285,7 +8438,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
     }
 
     private void verifyWifiInfoCopyNetCapsForCallerPermission(
@@ -8295,7 +8448,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName());
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
@@ -8414,11 +8567,7 @@
         final int myUid = Process.myUid();
         setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM);
 
-        try {
-            mService.getConnectionOwnerUid(getTestConnectionInfo());
-            fail("Expected SecurityException for non-VpnService app");
-        } catch (SecurityException expected) {
-        }
+        assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8426,11 +8575,7 @@
         final int myUid = Process.myUid();
         setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE);
 
-        try {
-            mService.getConnectionOwnerUid(getTestConnectionInfo());
-            fail("Expected SecurityException for non-VpnService app");
-        } catch (SecurityException expected) {
-        }
+        assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
 
     @Test
@@ -8643,7 +8788,7 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+        assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
         waitForIdle();
         assertTrue(
                 "Active VPN permission not applied",
@@ -8651,7 +8796,7 @@
                         Process.myPid(), Process.myUid(), naiWithoutUid,
                         mContext.getOpPackageName()));
 
-        assertTrue(mService.setUnderlyingNetworksForVpn(null));
+        assertTrue(mMockVpn.setUnderlyingNetworks(null));
         waitForIdle();
         assertFalse(
                 "VPN shouldn't receive callback on non-underlying network",
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 3a93c5b..cffd2d1d 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -74,6 +75,7 @@
 import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
+import android.net.VpnTransportInfo;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.os.Build.VERSION_CODES;
@@ -257,12 +259,14 @@
 
     @Test
     public void testRestrictedProfilesAreAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                null, null);
+
+        // Assume the user can have restricted profiles.
+        doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+        final Set<UidRange> ranges =
+                vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
                 PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -271,7 +275,6 @@
 
     @Test
     public void testManagedProfilesAreNotAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
@@ -294,7 +297,6 @@
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -320,7 +322,6 @@
 
     @Test
     public void testGetAlwaysAndOnGetLockDown() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
 
         // Default state.
@@ -345,7 +346,6 @@
 
     @Test
     public void testLockdownChangingPackage() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -373,7 +373,6 @@
 
     @Test
     public void testLockdownAllowlist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -448,7 +447,6 @@
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
                 new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -481,7 +479,6 @@
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] entireUser = {
             new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -958,14 +955,7 @@
     }
 
     private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
-        // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
-        // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
-        // setMockedUsers(primaryUser);
-        final ArrayList<UserInfo> users = new ArrayList<>();
-        users.add(primaryUser);
-        when(mUserManager.getAliveUsers()).thenReturn(users);
-        when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
-        when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
+        setMockedUsers(primaryUser);
 
         // Dummy egress interface
         final LinkProperties lp = new LinkProperties();
@@ -996,6 +986,13 @@
         startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve
     }
 
+    private void assertTransportInfoMatches(NetworkCapabilities nc, int type) {
+        assertNotNull(nc);
+        VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo();
+        assertNotNull(ti);
+        assertEquals(type, ti.type);
+    }
+
     public void startRacoon(final String serverAddr, final String expectedAddr)
             throws Exception {
         final ConditionVariable legacyRunnerReady = new ConditionVariable();
@@ -1032,8 +1029,10 @@
 
             // Now wait for the runner to be ready before testing for the route.
             ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+            ArgumentCaptor<NetworkCapabilities> ncCaptor =
+                    ArgumentCaptor.forClass(NetworkCapabilities.class);
             verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
-                    lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+                    lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt());
 
             // In this test the expected address is always v4 so /32.
             // Note that the interface needs to be specified because RouteInfo objects stored in
@@ -1043,6 +1042,8 @@
             final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
             assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
                     actualRoutes.contains(expectedRoute));
+
+            assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY);
         } finally {
             // Now interrupt the thread, unblock the runner and clean up.
             vpn.mVpnRunner.exitVpnRunner();
@@ -1199,11 +1200,6 @@
             final int id = (int) invocation.getArguments()[0];
             return userMap.get(id);
         }).when(mUserManager).getUserInfo(anyInt());
-
-        doAnswer(invocation -> {
-            final int id = (int) invocation.getArguments()[0];
-            return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
-        }).when(mUserManager).canHaveRestrictedProfile();
     }
 
     /**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index dde78aa..214c82d 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,8 +80,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
@@ -1456,8 +1454,6 @@
     }
 
     private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1465,7 +1461,7 @@
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         capabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
     private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1473,17 +1469,14 @@
     }
 
     private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
-        final NetworkInfo info = new NetworkInfo(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
-        info.setRoaming(isRoaming);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
+        return new NetworkState(
+                TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
     private NetworkStats buildEmptyStats() {
@@ -1491,11 +1484,9 @@
     }
 
     private static NetworkState buildVpnState() {
-        final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TUN_IFACE);
-        return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
+        return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
     }
 
     private long getElapsedRealtime() {
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index aeb142b..1fe13fe 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -28,8 +28,6 @@
 
 import junit.framework.TestCase;
 
-import org.junit.Test;
-
 /**
  * TODO: Remove this. This is only a placeholder, need to implement this.
  */
@@ -56,7 +54,7 @@
         }
 
         try {
-            mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
+            mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null /* options */);
             fail("IWindowManager.addWindowToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
@@ -155,29 +153,4 @@
             fail("Unexpected remote exception");
         }
     }
-
-    @Test
-    public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
-        // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
-        try {
-            mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
-            fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-
-        // Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
-        try {
-            mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
-            fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-    }
 }
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
 
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
+        return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+    }
+
+    // Public for use in VcnGatewayConnectionTest
+    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
                 new VcnGatewayConnectionConfig.Builder()
                         .setRetryInterval(RETRY_INTERVALS_MS)
                         .setMaxMtu(MAX_MTU);
 
-        for (int caps : EXPOSED_CAPS) {
+        for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
         }
 
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index f9db408..7dada9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -65,7 +65,7 @@
                 ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
         verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
 
-        assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+        assertTrue(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
 
         IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
         listenerWrapper.onPolicyChanged();
@@ -78,7 +78,7 @@
 
         mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
-        assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+        assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
         verify(mMockVcnManagementService)
                 .addVcnUnderlyingNetworkPolicyListener(
                         any(IVcnUnderlyingNetworkPolicyListener.class));
@@ -88,7 +88,7 @@
     public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
         mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
-        assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+        assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
         verify(mMockVcnManagementService, never())
                 .addVcnUnderlyingNetworkPolicyListener(
                         any(IVcnUnderlyingNetworkPolicyListener.class));
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e32e1e8..c290bff 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,6 +16,10 @@
 
 package com.android.server;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
 import static com.android.server.vcn.VcnTestUtils.setupSystemService;
@@ -66,6 +70,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
@@ -105,6 +110,7 @@
             Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
 
     private static final int TEST_SUBSCRIPTION_ID = 1;
+    private static final int TEST_SUBSCRIPTION_ID_2 = 2;
     private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
             new SubscriptionInfo(
                     TEST_SUBSCRIPTION_ID /* id */,
@@ -142,6 +148,9 @@
     private final TelephonySubscriptionTracker mSubscriptionTracker =
             mock(TelephonySubscriptionTracker.class);
 
+    private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+            ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
     private final VcnManagementService mVcnMgmtSvc;
 
     private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +193,7 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(Vcn.class);
-        }).when(mMockDeps).newVcn(any(), any(), any(), any());
+        }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
 
         final PersistableBundle bundle =
                 PersistableBundleUtils.fromMap(
@@ -307,7 +316,7 @@
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
         verify(mMockDeps)
-                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
+                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
     }
 
     @Test
@@ -485,7 +494,8 @@
                         eq(mVcnContext),
                         eq(TEST_UUID_2),
                         eq(TEST_VCN_CONFIG),
-                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
+                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+                        any());
 
         // Verify Vcn is updated if it was previously started
         mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -532,59 +542,121 @@
                 Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
     }
 
-    private void verifyMergedNetworkCapabilitiesIsVcnManaged(
-            NetworkCapabilities mergedCapabilities, @Transport int transportType) {
+    private void verifyMergedNetworkCapabilities(
+            NetworkCapabilities mergedCapabilities,
+            @Transport int transportType,
+            boolean isVcnManaged,
+            boolean isRestricted) {
         assertTrue(mergedCapabilities.hasTransport(transportType));
-        assertFalse(
+        assertEquals(
+                !isVcnManaged,
                 mergedCapabilities.hasCapability(
                         NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+        assertEquals(
+                !isRestricted,
+                mergedCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
+    }
+
+    private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) {
+        setUpVcnSubscription(subId, subGrp);
+        final Vcn vcn = startAndGetVcnInstance(subGrp);
+        doReturn(isVcnActive).when(vcn).isActive();
+    }
+
+    private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
+            int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
+        setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
+
+        final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
+        ncBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        if (transport == TRANSPORT_CELLULAR) {
+            ncBuilder
+                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID));
+        } else if (transport == TRANSPORT_WIFI) {
+            WifiInfo wifiInfo = mock(WifiInfo.class);
+            when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+            when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
+
+            ncBuilder
+                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                    .setTransportInfo(wifiInfo);
+        } else {
+            throw new IllegalArgumentException("Unknown transport");
+        }
+
+        return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties());
     }
 
     @Test
     public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
-        setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
-
-        NetworkCapabilities nc =
-                new NetworkCapabilities.Builder()
-                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                        .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
-                        .build();
-
-        VcnUnderlyingNetworkPolicy policy =
-                mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+        final VcnUnderlyingNetworkPolicy policy =
+                startVcnAndGetPolicyForTransport(
+                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR);
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilitiesIsVcnManaged(
-                policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+        verifyMergedNetworkCapabilities(
+                policy.getMergedNetworkCapabilities(),
+                TRANSPORT_CELLULAR,
+                true /* isVcnManaged */,
+                false /* isRestricted */);
+    }
+
+    @Test
+    public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception {
+        final VcnUnderlyingNetworkPolicy policy =
+                startVcnAndGetPolicyForTransport(
+                        TEST_SUBSCRIPTION_ID,
+                        TEST_UUID_2,
+                        false /* isActive */,
+                        TRANSPORT_CELLULAR);
+
+        assertFalse(policy.isTeardownRequested());
+        verifyMergedNetworkCapabilities(
+                policy.getMergedNetworkCapabilities(),
+                NetworkCapabilities.TRANSPORT_CELLULAR,
+                false /* isVcnManaged */,
+                false /* isRestricted */);
     }
 
     @Test
     public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
-        setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
-
-        WifiInfo wifiInfo = mock(WifiInfo.class);
-        when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
-        when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
-        NetworkCapabilities nc =
-                new NetworkCapabilities.Builder()
-                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                        .setTransportInfo(wifiInfo)
-                        .build();
-
-        VcnUnderlyingNetworkPolicy policy =
-                mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+        final VcnUnderlyingNetworkPolicy policy =
+                startVcnAndGetPolicyForTransport(
+                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI);
 
         assertFalse(policy.isTeardownRequested());
-        verifyMergedNetworkCapabilitiesIsVcnManaged(
-                policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+        verifyMergedNetworkCapabilities(
+                policy.getMergedNetworkCapabilities(),
+                NetworkCapabilities.TRANSPORT_WIFI,
+                true /* isVcnManaged */,
+                true /* isRestricted */);
+    }
+
+    @Test
+    public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception {
+        final VcnUnderlyingNetworkPolicy policy =
+                startVcnAndGetPolicyForTransport(
+                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
+
+        assertFalse(policy.isTeardownRequested());
+        verifyMergedNetworkCapabilities(
+                policy.getMergedNetworkCapabilities(),
+                NetworkCapabilities.TRANSPORT_WIFI,
+                false /* isVcnManaged */,
+                true /* isRestricted */);
     }
 
     @Test
     public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
+        setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */);
+
         NetworkCapabilities nc =
                 new NetworkCapabilities.Builder()
                         .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                        .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+                        .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                        .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2))
                         .build();
 
         VcnUnderlyingNetworkPolicy policy =
@@ -634,4 +706,25 @@
 
         verify(mMockPolicyListener).onPolicyChanged();
     }
+
+    @Test
+    public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_1),
+                        eq(TEST_VCN_CONFIG),
+                        eq(snapshot),
+                        mSafemodeCallbackCaptor.capture());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+        safemodeCallback.onEnteredSafemode();
+
+        assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e20070e..278d93a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -18,22 +18,35 @@
 
 import static android.net.IpSecManager.DIRECTION_IN;
 import static android.net.IpSecManager.DIRECTION_OUT;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
 
 /** Tests for VcnGatewayConnection.ConnectedState */
 @RunWith(AndroidJUnit4.class)
@@ -107,6 +120,51 @@
     }
 
     @Test
+    public void testChildOpenedRegistersNetwork() throws Exception {
+        final VcnChildSessionConfiguration mMockChildSessionConfig =
+                mock(VcnChildSessionConfiguration.class);
+        doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalAddresses();
+        doReturn(Collections.singletonList(TEST_DNS_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalDnsServers();
+
+        getChildSessionCallback().onOpened(mMockChildSessionConfig);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+                ArgumentCaptor.forClass(NetworkCapabilities.class);
+        verify(mConnMgr)
+                .registerNetworkAgent(
+                        any(),
+                        any(),
+                        lpCaptor.capture(),
+                        ncCaptor.capture(),
+                        anyInt(),
+                        any(),
+                        anyInt());
+        verify(mIpSecSvc)
+                .addAddressToTunnelInterface(
+                        eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+        final LinkProperties lp = lpCaptor.getValue();
+        assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses());
+        assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers());
+
+        final NetworkCapabilities nc = ncCaptor.getValue();
+        assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        for (int cap : mConfig.getAllExposedCapabilities()) {
+            assertTrue(nc.hasCapability(cap));
+        }
+    }
+
+    @Test
     public void testChildSessionClosedTriggersDisconnect() throws Exception {
         getChildSessionCallback().onClosed();
         mTestLooper.dispatchAll();
@@ -122,6 +180,4 @@
         assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
         verify(mIkeSession).close();
     }
-
-    // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index fbaae6f..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -45,7 +45,12 @@
     public void testEnterWhileNotRunningTriggersQuit() throws Exception {
         final VcnGatewayConnection vgc =
                 new VcnGatewayConnection(
-                        mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
 
         vgc.setIsRunning(false);
         vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
new file mode 100644
index 0000000..3f2b47c
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.RetryTimeoutState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testNewNetworkTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testNullNetworkTriggersDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testTimeoutElapsingTriggersRetry() throws Exception {
+        mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index df1341c..d449eab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,10 +27,13 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecTransform;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -43,15 +46,23 @@
 
 import com.android.server.IpSecService;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
 
+import java.net.InetAddress;
 import java.util.Collections;
 import java.util.UUID;
 
 public class VcnGatewayConnectionTestBase {
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+    protected static final InetAddress TEST_DNS_ADDR =
+            InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+    protected static final LinkAddress TEST_INTERNAL_ADDR =
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+
     protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
     protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
     protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
@@ -80,10 +91,12 @@
     @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
     @NonNull protected final VcnContext mVcnContext;
     @NonNull protected final VcnGatewayConnectionConfig mConfig;
+    @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
     @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
 
     @NonNull protected final IpSecService mIpSecSvc;
+    @NonNull protected final ConnectivityManager mConnMgr;
 
     protected VcnIkeSession mMockIkeSession;
     protected VcnGatewayConnection mGatewayConnection;
@@ -94,12 +107,17 @@
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
         mVcnContext = mock(VcnContext.class);
         mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+        mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
         mDeps = mock(VcnGatewayConnection.Dependencies.class);
         mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
 
         mIpSecSvc = mock(IpSecService.class);
         setupIpSecManager(mContext, mIpSecSvc);
 
+        mConnMgr = mock(ConnectivityManager.class);
+        VcnTestUtils.setupSystemService(
+                mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
         doReturn(mContext).when(mVcnContext).getContext();
         doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
@@ -123,7 +141,12 @@
 
         mGatewayConnection =
                 new VcnGatewayConnection(
-                        mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
     }
 
     protected IpSecTransform makeDummyIpSecTransform() throws Exception {
@@ -137,10 +160,10 @@
         return captor.getValue();
     }
 
-    protected ChildSessionCallback getChildSessionCallback() {
+    protected VcnChildSessionCallback getChildSessionCallback() {
         ArgumentCaptor<ChildSessionCallback> captor =
                 ArgumentCaptor.forClass(ChildSessionCallback.class);
         verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
-        return captor.getValue();
+        return (VcnChildSessionCallback) captor.getValue();
     }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 0c1df76..66cbf84 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,22 +16,27 @@
 
 package com.android.server.vcn;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.net.NetworkRequest;
 import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.os.ParcelUuid;
 import android.os.test.TestLooper;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
 
 import org.junit.Before;
@@ -51,9 +56,13 @@
     private VcnContext mVcnContext;
     private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
     private VcnNetworkProvider mVcnNetworkProvider;
+    private VcnSafemodeCallback mVcnSafemodeCallback;
     private Vcn.Dependencies mDeps;
 
+    private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
     private TestLooper mTestLooper;
+    private VcnGatewayConnectionConfig mGatewayConnectionConfig;
     private VcnConfig mConfig;
     private Vcn mVcn;
 
@@ -63,6 +72,7 @@
         mVcnContext = mock(VcnContext.class);
         mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+        mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
         mDeps = mock(Vcn.Dependencies.class);
 
         mTestLooper = new TestLooper();
@@ -76,15 +86,26 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(VcnGatewayConnection.class);
-        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
+        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
 
-        mConfig =
-                new VcnConfig.Builder(mContext)
-                        .addGatewayConnectionConfig(
-                                VcnGatewayConnectionConfigTest.buildTestConfig())
-                        .build();
+        mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
 
-        mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
+        final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            configBuilder.addGatewayConnectionConfig(
+                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+        }
+        configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+        mConfig = configBuilder.build();
+
+        mVcn =
+                new Vcn(
+                        mVcnContext,
+                        TEST_SUB_GROUP,
+                        mConfig,
+                        mSubscriptionSnapshot,
+                        mVcnSafemodeCallback,
+                        mDeps);
     }
 
     private NetworkRequestListener verifyAndGetRequestListener() {
@@ -95,23 +116,22 @@
         return mNetworkRequestListenerCaptor.getValue();
     }
 
-    private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
-        final NetworkRequest.Builder builder = new NetworkRequest.Builder();
-        for (final int netCapability : networkCapabilities) {
-            builder.addCapability(netCapability);
+    private void startVcnGatewayWithCapabilities(
+            NetworkRequestListener requestListener, int... netCapabilities) {
+        final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+        for (final int netCapability : netCapabilities) {
+            requestBuilder.addCapability(netCapability);
         }
-        return builder.build();
+
+        requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+        mTestLooper.dispatchAll();
     }
 
     @Test
     public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-
-        requestListener.onNetworkRequested(
-                getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
-                NETWORK_SCORE,
-                PROVIDER_ID);
-        mTestLooper.dispatchAll();
+        startVcnGatewayWithCapabilities(
+                requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
 
         final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
         assertFalse(gatewayConnections.isEmpty());
@@ -126,4 +146,38 @@
             verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
         }
     }
+
+    @Test
+    public void testGatewayEnteringSafemodeNotifiesVcn() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            startVcnGatewayWithCapabilities(requestListener, capability);
+        }
+
+        // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+        // Expect one VcnGatewayConnection per capability.
+        final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertEquals(numExpectedGateways, gatewayConnections.size());
+        verify(mDeps, times(numExpectedGateways))
+                .newVcnGatewayConnection(
+                        eq(mVcnContext),
+                        eq(TEST_SUB_GROUP),
+                        eq(mSubscriptionSnapshot),
+                        any(),
+                        mGatewayStatusCallbackCaptor.capture());
+
+        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+        // all Gateways
+        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+        statusCallback.onEnteredSafemode();
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+            verify(gatewayConnection).teardownAsynchronously();
+        }
+        verify(mVcnNetworkProvider).unregisterListener(requestListener);
+        verify(mVcnSafemodeCallback).onEnteredSafemode();
+    }
 }
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
deleted file mode 100755
index 28ff606..0000000
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""Generate API lists for non-SDK API enforcement."""
-import argparse
-from collections import defaultdict, namedtuple
-import functools
-import os
-import re
-import sys
-
-# Names of flags recognized by the `hiddenapi` tool.
-FLAG_SDK = 'sdk'
-FLAG_UNSUPPORTED = 'unsupported'
-FLAG_BLOCKED = 'blocked'
-FLAG_MAX_TARGET_O = 'max-target-o'
-FLAG_MAX_TARGET_P = 'max-target-p'
-FLAG_MAX_TARGET_Q = 'max-target-q'
-FLAG_MAX_TARGET_R = 'max-target-r'
-FLAG_CORE_PLATFORM_API = 'core-platform-api'
-FLAG_PUBLIC_API = 'public-api'
-FLAG_SYSTEM_API = 'system-api'
-FLAG_TEST_API = 'test-api'
-
-# List of all known flags.
-FLAGS_API_LIST = [
-    FLAG_SDK,
-    FLAG_UNSUPPORTED,
-    FLAG_BLOCKED,
-    FLAG_MAX_TARGET_O,
-    FLAG_MAX_TARGET_P,
-    FLAG_MAX_TARGET_Q,
-    FLAG_MAX_TARGET_R,
-]
-ALL_FLAGS = FLAGS_API_LIST + [
-    FLAG_CORE_PLATFORM_API,
-    FLAG_PUBLIC_API,
-    FLAG_SYSTEM_API,
-    FLAG_TEST_API,
-]
-
-FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
-ALL_FLAGS_SET = set(ALL_FLAGS)
-
-# Option specified after one of FLAGS_API_LIST to indicate that
-# only known and otherwise unassigned entries should be assign the
-# given flag.
-# For example, the max-target-P list is checked in as it was in P,
-# but signatures have changes since then. The flag instructs this
-# script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
-
-# Option specified after one of FLAGS_API_LIST to express that all
-# apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
-
-# Option specified after one of FLAGS_API_LIST to indicate an extra
-# tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
-
-# Regex patterns of fields/methods used in serialization. These are
-# considered public API despite being hidden.
-SERIALIZATION_PATTERNS = [
-    r'readObject\(Ljava/io/ObjectInputStream;\)V',
-    r'readObjectNoData\(\)V',
-    r'readResolve\(\)Ljava/lang/Object;',
-    r'serialVersionUID:J',
-    r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
-    r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
-    r'writeReplace\(\)Ljava/lang/Object;',
-]
-
-# Single regex used to match serialization API. It combines all the
-# SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
-
-# Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
-IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
-
-
-class StoreOrderedOptions(argparse.Action):
-    """An argparse action that stores a number of option arguments in the order that
-    they were specified.
-    """
-    def __call__(self, parser, args, values, option_string = None):
-        items = getattr(args, self.dest, None)
-        if items is None:
-            items = []
-        items.append([option_string.lstrip('-'), values])
-        setattr(args, self.dest, items)
-
-def get_args():
-    """Parses command line arguments.
-
-    Returns:
-        Namespace: dictionary of parsed arguments
-    """
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--output', required=True)
-    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
-        help='CSV files to be merged into output')
-
-    for flag in ALL_FLAGS:
-        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
-            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
-    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
-        'entries should be assign the given flag. Must follow a list of entries and applies '
-        'to the preceding such list.')
-    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
-        'is a list of packages. All members in those packages will be given the flag. '
-        'Must follow a list of entries and applies to the preceding such list.')
-    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
-        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
-        'Must follow a list of entries and applies to the preceding such list.')
-
-    return parser.parse_args()
-
-
-def read_lines(filename):
-    """Reads entire file and return it as a list of lines.
-
-    Lines which begin with a hash are ignored.
-
-    Args:
-        filename (string): Path to the file to read from.
-
-    Returns:
-        Lines of the file as a list of string.
-    """
-    with open(filename, 'r') as f:
-        lines = f.readlines();
-    lines = filter(lambda line: not line.startswith('#'), lines)
-    lines = map(lambda line: line.strip(), lines)
-    return set(lines)
-
-
-def write_lines(filename, lines):
-    """Writes list of lines into a file, overwriting the file if it exists.
-
-    Args:
-        filename (string): Path to the file to be writting into.
-        lines (list): List of strings to write into the file.
-    """
-    lines = map(lambda line: line + '\n', lines)
-    with open(filename, 'w') as f:
-        f.writelines(lines)
-
-
-def extract_package(signature):
-    """Extracts the package from a signature.
-
-    Args:
-        signature (string): JNI signature of a method or field.
-
-    Returns:
-        The package name of the class containing the field/method.
-    """
-    full_class_name = signature.split(";->")[0]
-    # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
-    if (full_class_name[0] != "L"):
-        raise ValueError("Expected to start with 'L': %s" % full_class_name)
-    full_class_name = full_class_name[1:]
-    # If full_class_name doesn't contain '/', then package_name will be ''.
-    package_name = full_class_name.rpartition("/")[0]
-    return package_name.replace('/', '.')
-
-
-class FlagsDict:
-    def __init__(self):
-        self._dict_keyset = set()
-        self._dict = defaultdict(set)
-
-    def _check_entries_set(self, keys_subset, source):
-        assert isinstance(keys_subset, set)
-        assert keys_subset.issubset(self._dict_keyset), (
-            "Error: {} specifies signatures not present in code:\n"
-            "{}"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
-
-    def _check_flags_set(self, flags_subset, source):
-        assert isinstance(flags_subset, set)
-        assert flags_subset.issubset(ALL_FLAGS_SET), (
-            "Error processing: {}\n"
-            "The following flags were not recognized: \n"
-            "{}\n"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - ALL_FLAGS_SET))
-
-    def filter_apis(self, filter_fn):
-        """Returns APIs which match a given predicate.
-
-        This is a helper function which allows to filter on both signatures (keys) and
-        flags (values). The built-in filter() invokes the lambda only with dict's keys.
-
-        Args:
-            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
-
-        Returns:
-            A set of APIs which match the predicate.
-        """
-        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-
-    def get_valid_subset_of_unassigned_apis(self, api_subset):
-        """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any API list flags.
-
-        Args:
-            entries_subset (set/list): Key set to be sanitized.
-
-        Returns:
-            Sanitized key set.
-        """
-        assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
-    def generate_csv(self):
-        """Constructs CSV entries from a dictionary.
-
-        Old versions of flags are used to generate the file.
-
-        Returns:
-            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
-        """
-        lines = []
-        for api in self._dict:
-          flags = sorted(self._dict[api])
-          lines.append(",".join([api] + flags))
-        return sorted(lines)
-
-    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
-        """Parses CSV entries and merges them into a given dictionary.
-
-        The expected CSV format is:
-            <api signature>,<flag1>,<flag2>,...,<flagN>
-
-        Args:
-            csv_lines (list of strings): Lines read from a CSV file.
-            source (string): Origin of `csv_lines`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed flags are invalid.
-        """
-        # Split CSV lines into arrays of values.
-        csv_values = [ line.split(',') for line in csv_lines ]
-
-        # Update the full set of API signatures.
-        self._dict_keyset.update([ csv[0] for csv in csv_values ])
-
-        # Check that all flags are known.
-        csv_flags = set()
-        for csv in csv_values:
-          csv_flags.update(csv[1:])
-        self._check_flags_set(csv_flags, source)
-
-        # Iterate over all CSV lines, find entry in dict and append flags to it.
-        for csv in csv_values:
-            flags = csv[1:]
-            if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
-                flags.append(FLAG_SDK)
-            self._dict[csv[0]].update(flags)
-
-    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
-        """Assigns a flag to given subset of entries.
-
-        Args:
-            flag (string): One of ALL_FLAGS.
-            apis (set): Subset of APIs to receive the flag.
-            source (string): Origin of `entries_subset`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed API signatures of flags are invalid.
-        """
-        # Check that all APIs exist in the dict.
-        self._check_entries_set(apis, source)
-
-        # Check that the flag is known.
-        self._check_flags_set(set([ flag ]), source)
-
-        # Iterate over the API subset, find each entry in dict and assign the flag to it.
-        for api in apis:
-            self._dict[api].add(flag)
-            if tag:
-                self._dict[api].add(tag)
-
-
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
-
-def parse_ordered_flags(ordered_flags):
-    r = []
-    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
-    for flag_value in ordered_flags:
-        flag, value = flag_value[0], flag_value[1]
-        if flag in ALL_FLAGS_SET:
-            if currentflag:
-                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
-                ignore_conflicts, packages, tag = False, False, None
-            currentflag = flag
-            file = value
-        else:
-            if currentflag is None:
-                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
-                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
-            if flag == FLAG_IGNORE_CONFLICTS:
-                ignore_conflicts = True
-            elif flag == FLAG_PACKAGES:
-                packages = True
-            elif flag == FLAG_TAG:
-                tag = value[0]
-
-
-    if currentflag:
-        r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
-    return r
-
-
-def main(argv):
-    # Parse arguments.
-    args = vars(get_args())
-    flagfiles = parse_ordered_flags(args['ordered_flags'])
-
-    # Initialize API->flags dictionary.
-    flags = FlagsDict()
-
-    # Merge input CSV files into the dictionary.
-    # Do this first because CSV files produced by parsing API stubs will
-    # contain the full set of APIs. Subsequent additions from text files
-    # will be able to detect invalid entries, and/or filter all as-yet
-    # unassigned entries.
-    for filename in args["csv"]:
-        flags.parse_and_merge_csv(read_lines(filename), filename)
-
-    # Combine inputs which do not require any particular order.
-    # (1) Assign serialization API to SDK.
-    flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
-
-    # (2) Merge text files with a known flag into the dictionary.
-    for info in flagfiles:
-        if (not info.ignore_conflicts) and (not info.packages):
-            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
-
-    # Merge text files where conflicts should be ignored.
-    # This will only assign the given flag if:
-    # (a) the entry exists, and
-    # (b) it has not been assigned any other flag.
-    # Because of (b), this must run after all strict assignments have been performed.
-    for info in flagfiles:
-        if info.ignore_conflicts:
-            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
-            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
-
-    # All members in the specified packages will be assigned the appropriate flag.
-    for info in flagfiles:
-        if info.packages:
-            packages_needing_list = set(read_lines(info.file))
-            should_add_signature_to_list = lambda sig,lists: extract_package(
-                sig) in packages_needing_list and not lists
-            valid_entries = flags.filter_apis(should_add_signature_to_list)
-            flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
-
-    # Mark all remaining entries as blocked.
-    flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
-    # Write output.
-    write_lines(args["output"], flags.generate_csv())
-
-if __name__ == "__main__":
-    main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
deleted file mode 100755
index 82d117f..0000000
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""Unit tests for Hidden API list generation."""
-import unittest
-from generate_hiddenapi_lists import *
-
-class TestHiddenapiListGeneration(unittest.TestCase):
-
-    def test_filter_apis(self):
-        # Initialize flags so that A and B are put on the whitelist and
-        # C, D, E are left unassigned. Try filtering for the unassigned ones.
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
-                        'C', 'D', 'E'])
-        filter_set = flags.filter_apis(lambda api, flags: not flags)
-        self.assertTrue(isinstance(filter_set, set))
-        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
-
-    def test_get_valid_subset_of_unassigned_keys(self):
-        # Create flags where only A is unassigned.
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
-        flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
-
-        # Check three things:
-        # (1) B is selected as valid unassigned
-        # (2) A is not selected because it is assigned 'whitelist'
-        # (3) D is not selected because it is not a valid key
-        self.assertEqual(
-            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
-
-    def test_parse_and_merge_csv(self):
-        flags = FlagsDict()
-
-        # Test empty CSV entry.
-        self.assertEqual(flags.generate_csv(), [])
-
-        # Test new additions.
-        flags.parse_and_merge_csv([
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-        self.assertEqual(flags.generate_csv(), [
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-
-        # Test unknown flag.
-        with self.assertRaises(AssertionError):
-            flags.parse_and_merge_csv([ 'Z,foo' ])
-
-    def test_assign_flag(self):
-        flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
-
-        # Test new additions.
-        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ])
-
-        # Test invalid API signature.
-        with self.assertRaises(AssertionError):
-            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
-
-        # Test invalid flag.
-        with self.assertRaises(AssertionError):
-            flags.assign_flag('foo', set([ 'A' ]))
-
-    def test_extract_package(self):
-        signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
-        expected_package = 'com.foo.bar'
-        self.assertEqual(extract_package(signature), expected_package)
-
-        signature = 'Lcom/foo1/bar/MyClass;->method2()V'
-        expected_package = 'com.foo1.bar'
-        self.assertEqual(extract_package(signature), expected_package)
-
-        signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
-        expected_package = 'com.foo_bar.baz'
-        self.assertEqual(extract_package(signature), expected_package)
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
deleted file mode 100755
index 6a5b0e1..0000000
--- a/tools/hiddenapi/merge_csv.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""
-Merge multiple CSV files, possibly with different columns.
-"""
-
-import argparse
-import csv
-import io
-
-from zipfile import ZipFile
-
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
-                                          'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
-                         default='-', type=argparse.FileType('w'))
-args_parser.add_argument('files', nargs=argparse.REMAINDER)
-args = args_parser.parse_args()
-
-
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|')
-
-
-if args.zip_input and len(args.files) > 0:
-    raise ValueError('Expecting either a single ZIP with CSV files'
-                     ' or a list of CSV files as input; not both.')
-
-csv_readers = []
-if len(args.files) > 0:
-    for file in args.files:
-        csv_readers.append(dict_reader(open(file, 'r')))
-elif args.zip_input:
-    with ZipFile(args.zip_input) as zip:
-        for entry in zip.namelist():
-            if entry.endswith('.uau'):
-                csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-
-headers = set()
-if args.header:
-    fieldnames = args.header.split(',')
-else:
-    # Build union of all columns from source files:
-    for reader in csv_readers:
-        headers = headers.union(reader.fieldnames)
-    fieldnames = sorted(headers)
-
-# Concatenate all files to output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                        dialect='unix', fieldnames=fieldnames)
-writer.writeheader()
-for reader in csv_readers:
-    for row in reader:
-        writer.writerow(row)