Merge "Add cheap aidl tracing suitable for always-on-tracing (1/3)"
diff --git a/Android.bp b/Android.bp
index 95cdea0..54983d6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,7 +69,7 @@
         // Java/AIDL sources under frameworks/base
         ":framework-annotations",
         ":framework-blobstore-sources",
-        ":framework-connectivity-nsd-sources",
+        ":framework-connectivity-tiramisu-sources",
         ":framework-core-sources",
         ":framework-drm-sources",
         ":framework-graphics-nonupdatable-sources",
@@ -156,6 +156,7 @@
         "framework-scheduling.stubs.module_lib",
         "framework-sdkextensions.stubs.module_lib",
         "framework-statsd.stubs.module_lib",
+        "framework-supplementalapi.stubs.module_lib",
         "framework-supplementalprocess.stubs.module_lib",
         "framework-tethering.stubs.module_lib",
         "framework-uwb.stubs.module_lib",
@@ -180,6 +181,7 @@
         "framework-scheduling.impl",
         "framework-sdkextensions.impl",
         "framework-statsd.impl",
+        "framework-supplementalapi.impl",
         "framework-supplementalprocess.impl",
         "framework-tethering.impl",
         "framework-uwb.impl",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3b11036..92c63c9 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,6 +250,7 @@
     "framework-scheduling.stubs",
     "framework-sdkextensions.stubs",
     "framework-statsd.stubs",
+    "framework-supplementalapi.stubs",
     "framework-supplementalprocess.stubs",
     "framework-tethering.stubs",
     "framework-uwb.stubs",
@@ -272,6 +273,7 @@
     "framework-scheduling.stubs.system",
     "framework-sdkextensions.stubs.system",
     "framework-statsd.stubs.system",
+    "framework-supplementalapi.stubs",
     "framework-supplementalprocess.stubs",
     "framework-tethering.stubs.system",
     "framework-uwb.stubs.system",
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index af4053f..05a06619 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -105,6 +105,7 @@
 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
 static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
 static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
+static const char CLOCK_ENABLED_PROP_NAME[] = "persist.sys.bootanim.clock.enabled";
 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
 static constexpr size_t TEXT_POS_LEN_MAX = 16;
 static const int DYNAMIC_COLOR_COUNT = 4;
@@ -1320,6 +1321,8 @@
     }
     if (!anyPartHasClock) {
         mClockEnabled = false;
+    } else if (!android::base::GetBoolProperty(CLOCK_ENABLED_PROP_NAME, false)) {
+        mClockEnabled = false;
     }
 
     // Check if npot textures are supported
diff --git a/core/api/current.txt b/core/api/current.txt
index a65cf949..154c42f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8757,10 +8757,10 @@
     method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getScanMode();
     method public int getState();
-    method public int isCisCentralSupported();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
     method public boolean isEnabled();
     method public boolean isLe2MPhySupported();
+    method public int isLeAudioSupported();
     method public boolean isLeCodedPhySupported();
     method public boolean isLeExtendedAdvertisingSupported();
     method public boolean isLePeriodicAdvertisingSupported();
@@ -10127,8 +10127,10 @@
     ctor public CompanionDeviceService();
     method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void dispatchMessage(int, int, @NonNull byte[]);
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @MainThread public abstract void onDeviceAppeared(@NonNull String);
-    method @MainThread public abstract void onDeviceDisappeared(@NonNull String);
+    method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String);
+    method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo);
+    method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String);
+    method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo);
     method @MainThread public void onDispatchMessage(int, int, @NonNull byte[]);
     field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService";
   }
@@ -12804,7 +12806,8 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract java.util.List<android.content.pm.PermissionGroupInfo> getAllPermissionGroups(int);
@@ -12813,7 +12816,8 @@
     method public abstract int getApplicationEnabledSetting(@NonNull String);
     method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull android.content.pm.ApplicationInfo);
     method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12824,9 +12828,11 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
     method public void getGroupOfPlatformPermission(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.String>);
     method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+    method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags);
     method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
-    method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+    method @NonNull public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
     method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String);
     method @NonNull public abstract byte[] getInstantAppCookie();
     method public abstract int getInstantAppCookieMaxBytes();
@@ -12837,15 +12843,21 @@
     method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
     method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract String getNameForUid(int);
-    method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
+    method @Deprecated @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
+    method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
     method public abstract int[] getPackageGids(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Nullable public int[] getPackageGids(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.pm.PackageInstaller getPackageInstaller();
-    method public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public int getPackageUid(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract String[] getPackagesForUid(int);
-    method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], int);
+    method @NonNull public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(@NonNull String[], @NonNull android.content.pm.PackageManager.PackageInfoFlags);
     method @NonNull public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void getPlatformPermissionsForGroup(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
@@ -12853,14 +12865,18 @@
     method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
     method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+    method @Deprecated @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, @NonNull android.content.pm.PackageManager.ComponentInfoFlags) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
+    method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(@NonNull android.content.pm.PackageManager.PackageInfoFlags);
     method @Nullable public android.os.Bundle getSuspendedPackageAppExtras();
     method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
     method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
@@ -12888,13 +12904,19 @@
     method public abstract boolean isSafeMode();
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String);
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String);
-    method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
-    method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int);
+    method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int);
+    method @NonNull public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
     method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int);
-    method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
-    method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
-    method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
-    method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, int);
+    method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
+    method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable java.util.List<android.content.Intent>, @NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
+    method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
+    method @NonNull public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
     method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@Nullable String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
     method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String);
@@ -12903,9 +12925,12 @@
     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.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);
+    method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
+    method @Nullable public android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
+    method @Deprecated @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
+    method @Nullable public android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, @NonNull android.content.pm.PackageManager.ComponentInfoFlags);
+    method @Deprecated @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
+    method @Nullable public android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags);
     method public abstract void setApplicationCategoryHint(@NonNull String, int);
     method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
     method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
@@ -13107,6 +13132,11 @@
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
   }
 
+  public static final class PackageManager.ApplicationInfoFlags {
+    method public long getValue();
+    method @NonNull public static android.content.pm.PackageManager.ApplicationInfoFlags of(long);
+  }
+
   public static final class PackageManager.ComponentEnabledSetting implements android.os.Parcelable {
     ctor public PackageManager.ComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
     method public int describeContents();
@@ -13117,6 +13147,11 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.ComponentEnabledSetting> CREATOR;
   }
 
+  public static final class PackageManager.ComponentInfoFlags {
+    method public long getValue();
+    method @NonNull public static android.content.pm.PackageManager.ComponentInfoFlags of(long);
+  }
+
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
     ctor public PackageManager.NameNotFoundException();
     ctor public PackageManager.NameNotFoundException(String);
@@ -13126,6 +13161,11 @@
     method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
   }
 
+  public static final class PackageManager.PackageInfoFlags {
+    method public long getValue();
+    method @NonNull public static android.content.pm.PackageManager.PackageInfoFlags of(long);
+  }
+
   public static final class PackageManager.Property implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getBoolean();
@@ -13145,6 +13185,11 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.Property> CREATOR;
   }
 
+  public static final class PackageManager.ResolveInfoFlags {
+    method public long getValue();
+    method @NonNull public static android.content.pm.PackageManager.ResolveInfoFlags of(long);
+  }
+
   @Deprecated public class PackageStats implements android.os.Parcelable {
     ctor @Deprecated public PackageStats(String);
     ctor @Deprecated public PackageStats(android.os.Parcel);
@@ -31934,7 +31979,7 @@
     method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
     method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
-    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
@@ -31944,7 +31989,7 @@
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
     method @Deprecated @Nullable public java.io.Serializable readSerializable();
-    method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
     method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32477,7 +32522,7 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount();
     method public long getUserCreationTime(android.os.UserHandle);
     method public android.os.UserHandle getUserForSerialNumber(long);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName();
+    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS", "android.permission.QUERY_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
@@ -43237,6 +43282,7 @@
     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(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(boolean, boolean);
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -43289,8 +43335,10 @@
     method public boolean isWorldPhone();
     method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
     method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
+    method public void registerTelephonyCallback(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(boolean, @NonNull android.telephony.NetworkScanRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
     method public String sendEnvelopeWithStatus(String);
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b8ce02e7..7756906 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -215,6 +215,10 @@
 
 package android.net {
 
+  public final class ConnectivityFrameworkInitializerTiramisu {
+    method public static void registerServiceWrappers();
+  }
+
   public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public EthernetNetworkSpecifier(@NonNull String);
     method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c1d1518..c9b8ba2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -219,6 +219,7 @@
     field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
     field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
     field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+    field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
     field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
     field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
     field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
@@ -2427,6 +2428,8 @@
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int);
+    method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
   }
 
@@ -2896,13 +2899,16 @@
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
     method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.pm.dex.ArtManager getArtManager();
-    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags);
     method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public CharSequence getHarmfulAppWarning(@NonNull String);
     method @Nullable public String getIncidentReportApproverPackageName();
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(@NonNull android.content.pm.PackageManager.PackageInfoFlags, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract android.graphics.drawable.Drawable getInstantAppIcon(String);
     method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent();
     method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent();
@@ -2914,10 +2920,14 @@
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
     method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
@@ -3889,6 +3899,127 @@
 
 }
 
+package android.hardware.input {
+
+  public final class VirtualKeyEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAction();
+    method public int getKeyCode();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ACTION_DOWN = 0; // 0x0
+    field public static final int ACTION_UP = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualKeyEvent> CREATOR;
+  }
+
+  public static final class VirtualKeyEvent.Builder {
+    ctor public VirtualKeyEvent.Builder();
+    method @NonNull public android.hardware.input.VirtualKeyEvent build();
+    method @NonNull public android.hardware.input.VirtualKeyEvent.Builder setAction(int);
+    method @NonNull public android.hardware.input.VirtualKeyEvent.Builder setKeyCode(int);
+  }
+
+  public class VirtualKeyboard implements java.io.Closeable {
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
+  }
+
+  public class VirtualMouse implements java.io.Closeable {
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
+  }
+
+  public final class VirtualMouseButtonEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAction();
+    method public int getButtonCode();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
+    field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
+    field public static final int BUTTON_BACK = 8; // 0x8
+    field public static final int BUTTON_FORWARD = 16; // 0x10
+    field public static final int BUTTON_PRIMARY = 1; // 0x1
+    field public static final int BUTTON_SECONDARY = 2; // 0x2
+    field public static final int BUTTON_TERTIARY = 4; // 0x4
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseButtonEvent> CREATOR;
+  }
+
+  public static final class VirtualMouseButtonEvent.Builder {
+    ctor public VirtualMouseButtonEvent.Builder();
+    method @NonNull public android.hardware.input.VirtualMouseButtonEvent build();
+    method @NonNull public android.hardware.input.VirtualMouseButtonEvent.Builder setAction(int);
+    method @NonNull public android.hardware.input.VirtualMouseButtonEvent.Builder setButtonCode(int);
+  }
+
+  public final class VirtualMouseRelativeEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getRelativeX();
+    method public float getRelativeY();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseRelativeEvent> CREATOR;
+  }
+
+  public static final class VirtualMouseRelativeEvent.Builder {
+    ctor public VirtualMouseRelativeEvent.Builder();
+    method @NonNull public android.hardware.input.VirtualMouseRelativeEvent build();
+    method @NonNull public android.hardware.input.VirtualMouseRelativeEvent.Builder setRelativeX(float);
+    method @NonNull public android.hardware.input.VirtualMouseRelativeEvent.Builder setRelativeY(float);
+  }
+
+  public final class VirtualMouseScrollEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getXAxisMovement();
+    method public float getYAxisMovement();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualMouseScrollEvent> CREATOR;
+  }
+
+  public static final class VirtualMouseScrollEvent.Builder {
+    ctor public VirtualMouseScrollEvent.Builder();
+    method @NonNull public android.hardware.input.VirtualMouseScrollEvent build();
+    method @NonNull public android.hardware.input.VirtualMouseScrollEvent.Builder setXAxisMovement(@FloatRange(from=-1.0F, to=1.0f) float);
+    method @NonNull public android.hardware.input.VirtualMouseScrollEvent.Builder setYAxisMovement(@FloatRange(from=-1.0F, to=1.0f) float);
+  }
+
+  public final class VirtualTouchEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAction();
+    method public float getMajorAxisSize();
+    method public int getPointerId();
+    method public float getPressure();
+    method public int getToolType();
+    method public float getX();
+    method public float getY();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ACTION_CANCEL = 3; // 0x3
+    field public static final int ACTION_DOWN = 0; // 0x0
+    field public static final int ACTION_MOVE = 2; // 0x2
+    field public static final int ACTION_UP = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualTouchEvent> CREATOR;
+    field public static final int TOOL_TYPE_FINGER = 1; // 0x1
+    field public static final int TOOL_TYPE_PALM = 5; // 0x5
+  }
+
+  public static final class VirtualTouchEvent.Builder {
+    ctor public VirtualTouchEvent.Builder();
+    method @NonNull public android.hardware.input.VirtualTouchEvent build();
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setAction(int);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setMajorAxisSize(@FloatRange(from=0.0f) float);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(int);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPressure(@FloatRange(from=0.0f) float);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setToolType(int);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setX(float);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setY(float);
+  }
+
+  public class VirtualTouchscreen implements java.io.Closeable {
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
+  }
+
+}
+
 package android.hardware.lights {
 
   public final class LightState implements android.os.Parcelable {
@@ -3910,6 +4041,7 @@
   public class ContextHubClient implements java.io.Closeable {
     method public void close();
     method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
+    method public int getId();
     method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
   }
 
@@ -9051,31 +9183,31 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.NewUserResponse createUser(@NonNull android.os.NewUserRequest);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
+    method @NonNull public java.util.List<android.os.UserHandle> getAllProfiles();
+    method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
-    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
-    method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
+    method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
     method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isAdminUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser();
+    method public boolean isProfile();
     method public boolean isRestrictedProfile();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
@@ -9568,6 +9700,7 @@
     field public static final String NAMESPACE_TELEPHONY = "telephony";
     field public static final String NAMESPACE_TETHERING = "tethering";
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
+    field public static final String NAMESPACE_UWB = "uwb";
     field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
   }
 
@@ -13901,12 +14034,14 @@
   }
 
   public final class RcsClientConfiguration implements android.os.Parcelable {
-    ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+    ctor @Deprecated public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+    ctor public RcsClientConfiguration(@NonNull String, @NonNull String, @NonNull String, @NonNull String, boolean);
     method public int describeContents();
     method @NonNull public String getClientVendor();
     method @NonNull public String getClientVersion();
     method @NonNull public String getRcsProfile();
     method @NonNull public String getRcsVersion();
+    method public boolean isRcsEnabledByUser();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
     field public static final String RCS_PROFILE_1_0 = "UP_1.0";
@@ -14043,6 +14178,7 @@
     field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
     field public static final int PUBLISH_STATE_OK = 1; // 0x1
     field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+    field public static final int PUBLISH_STATE_PUBLISHING = 7; // 0x7
     field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
     field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
     field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
@@ -14305,6 +14441,7 @@
 package android.telephony.ims.stub {
 
   public interface CapabilityExchangeEventListener {
+    method public default void onPublishUpdated(int, @NonNull String, int, @NonNull String) throws android.telephony.ims.ImsException;
     method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
     method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
     method public void onUnpublish() throws android.telephony.ims.ImsException;
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 2c5acf1..9cb9ddc 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -60,6 +60,14 @@
 
 }
 
+package android.bluetooth {
+
+  public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+  }
+
+}
+
 package android.content {
 
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bf06db0..a921500 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -685,6 +685,14 @@
 
 }
 
+package android.companion {
+
+  public abstract class CompanionDeviceService extends android.app.Service {
+    method public void onBindCompanionDeviceService(@NonNull android.content.Intent);
+  }
+
+}
+
 package android.content {
 
   public final class AttributionSource implements android.os.Parcelable {
@@ -818,7 +826,8 @@
     method @Nullable public String getDefaultTextClassifierPackageName();
     method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
     method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
-    method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+    method @Deprecated @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+    method @NonNull public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(@NonNull android.content.pm.PackageManager.ApplicationInfoFlags, int);
     method @Nullable public abstract String[] getNamesForUids(int[]);
     method @NonNull public String getPermissionControllerPackageName();
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
@@ -1817,7 +1826,7 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
-    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
     method public static boolean isGuestUserEphemeral();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ad62872..a642696 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -521,7 +521,9 @@
     public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
 
     /**
-     * Action to toggle docking the current app's window
+     * Action to toggle docking the current app's window.
+     * <p>
+     * <strong>Note:</strong>  It is effective only if it appears in {@link #getSystemActions()}.
      */
     public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 01cd969..c0a8c1e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,10 +38,8 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
-import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UptimeMillisLong;
 import android.app.RemoteServiceException.BadForegroundServiceNotificationException;
 import android.app.RemoteServiceException.CannotDeliverBroadcastException;
 import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
@@ -323,7 +321,8 @@
 
     @UnsupportedAppUsage
     private ContextImpl mSystemContext;
-    private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
+    @GuardedBy("this")
+    private SparseArray<ContextImpl> mDisplaySystemUiContexts;
 
     @UnsupportedAppUsage
     static volatile IPackageManager sPackageManager;
@@ -2547,9 +2546,18 @@
 
             if (packageInfo != null) {
                 if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
-                    List<String> oldPaths = new ArrayList<>();
-                    LoadedApk.makePaths(this, aInfo, oldPaths);
-                    packageInfo.updateApplicationInfo(aInfo, oldPaths);
+                    if (packageInfo.getApplicationInfo().createTimestamp > aInfo.createTimestamp) {
+                        // The cached loaded apk is newer than the one passed in, we should not
+                        // update the cached version
+                        Slog.w(TAG, "getPackageInfo() called with an older ApplicationInfo "
+                                + "than the cached version for package " + aInfo.packageName);
+                    } else {
+                        Slog.v(TAG, "getPackageInfo() caused update to cached ApplicationInfo "
+                                + "for package " + aInfo.packageName);
+                        List<String> oldPaths = new ArrayList<>();
+                        LoadedApk.makePaths(this, aInfo, oldPaths);
+                        packageInfo.updateApplicationInfo(aInfo, oldPaths);
+                    }
                 }
 
                 return packageInfo;
@@ -2658,7 +2666,6 @@
         }
     }
 
-    @Override
     @NonNull
     public ContextImpl getSystemUiContext() {
         return getSystemUiContext(DEFAULT_DISPLAY);
@@ -2672,6 +2679,9 @@
     @NonNull
     public ContextImpl getSystemUiContext(int displayId) {
         synchronized (this) {
+            if (mDisplaySystemUiContexts == null) {
+                mDisplaySystemUiContexts = new SparseArray<>();
+            }
             ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
             if (systemUiContext == null) {
                 systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
@@ -2681,6 +2691,25 @@
         }
     }
 
+    @Nullable
+    @Override
+    public ContextImpl getSystemUiContextNoCreate() {
+        synchronized (this) {
+            if (mDisplaySystemUiContexts == null) return null;
+            return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY);
+        }
+    }
+
+    void onSystemUiContextCleanup(ContextImpl context) {
+        synchronized (this) {
+            if (mDisplaySystemUiContexts == null) return;
+            final int index = mDisplaySystemUiContexts.indexOfValue(context);
+            if (index >= 0) {
+                mDisplaySystemUiContexts.removeAt(index);
+            }
+        }
+    }
+
     public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
         synchronized (this) {
             getSystemContext().installSystemApplicationInfo(info, classLoader);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index bc698f6..b9ad5c3 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -28,7 +28,7 @@
 interface ActivityThreadInternal {
     ContextImpl getSystemContext();
 
-    ContextImpl getSystemUiContext();
+    ContextImpl getSystemUiContextNoCreate();
 
     boolean isInDensityCompatMode();
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index d1b7145..44fb5db 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -210,16 +210,28 @@
     @Override
     public PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException {
+        return getPackageInfo(packageName, PackageInfoFlags.of(flags));
+    }
+
+    @Override
+    public PackageInfo getPackageInfo(String packageName, PackageInfoFlags flags)
+            throws NameNotFoundException {
         return getPackageInfoAsUser(packageName, flags, getUserId());
     }
 
     @Override
     public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags)
             throws NameNotFoundException {
+        return getPackageInfo(versionedPackage, PackageInfoFlags.of(flags));
+    }
+
+    @Override
+    public PackageInfo getPackageInfo(VersionedPackage versionedPackage, PackageInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage,
-                    updateFlagsForPackage(flags, userId), userId);
+                    updateFlagsForPackage(flags.getValue(), userId), userId);
             if (pi != null) {
                 return pi;
             }
@@ -232,10 +244,16 @@
     @Override
     public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
+        return getPackageInfoAsUser(packageName, PackageInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    public PackageInfo getPackageInfoAsUser(String packageName, PackageInfoFlags flags, int userId)
+            throws NameNotFoundException {
         PackageInfo pi =
                 getPackageInfoAsUserCached(
                         packageName,
-                        updateFlagsForPackage(flags, userId),
+                        updateFlagsForPackage(flags.getValue(), userId),
                         userId);
         if (pi == null) {
             throw new NameNotFoundException(packageName);
@@ -334,10 +352,16 @@
     @Override
     public int[] getPackageGids(String packageName, int flags)
             throws NameNotFoundException {
+        return getPackageGids(packageName, PackageInfoFlags.of(flags));
+    }
+
+    @Override
+    public int[] getPackageGids(String packageName, PackageInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             int[] gids = mPM.getPackageGids(packageName,
-                    updateFlagsForPackage(flags, userId), userId);
+                    updateFlagsForPackage(flags.getValue(), userId), userId);
             if (gids != null) {
                 return gids;
             }
@@ -350,6 +374,12 @@
 
     @Override
     public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
+        return getPackageUid(packageName, PackageInfoFlags.of(flags));
+    }
+
+    @Override
+    public int getPackageUid(String packageName, PackageInfoFlags flags)
+            throws NameNotFoundException {
         return getPackageUidAsUser(packageName, flags, getUserId());
     }
 
@@ -361,9 +391,15 @@
     @Override
     public int getPackageUidAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
+        return getPackageUidAsUser(packageName, PackageInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    public int getPackageUidAsUser(String packageName, PackageInfoFlags flags, int userId)
+            throws NameNotFoundException {
         try {
             int uid = mPM.getPackageUid(packageName,
-                    updateFlagsForPackage(flags, userId), userId);
+                    updateFlagsForPackage(flags.getValue(), userId), userId);
             if (uid >= 0) {
                 return uid;
             }
@@ -448,15 +484,27 @@
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
+        return getApplicationInfo(packageName, ApplicationInfoFlags.of(flags));
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo(String packageName, ApplicationInfoFlags flags)
+            throws NameNotFoundException {
         return getApplicationInfoAsUser(packageName, flags, getUserId());
     }
 
     @Override
     public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
+        return getApplicationInfoAsUser(packageName, ApplicationInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfoAsUser(String packageName, ApplicationInfoFlags flags,
+            int userId) throws NameNotFoundException {
         ApplicationInfo ai = getApplicationInfoAsUserCached(
                         packageName,
-                        updateFlagsForApplication(flags, userId),
+                        updateFlagsForApplication(flags.getValue(), userId),
                         userId);
         if (ai == null) {
             throw new NameNotFoundException(packageName);
@@ -505,10 +553,16 @@
     @Override
     public ActivityInfo getActivityInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        return getActivityInfo(className, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             ActivityInfo ai = mPM.getActivityInfo(className,
-                    updateFlagsForComponent(flags, userId, null), userId);
+                    updateFlagsForComponent(flags.getValue(), userId, null), userId);
             if (ai != null) {
                 return ai;
             }
@@ -522,10 +576,16 @@
     @Override
     public ActivityInfo getReceiverInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        return getReceiverInfo(className, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public ActivityInfo getReceiverInfo(ComponentName className, ComponentInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             ActivityInfo ai = mPM.getReceiverInfo(className,
-                    updateFlagsForComponent(flags, userId, null), userId);
+                    updateFlagsForComponent(flags.getValue(), userId, null), userId);
             if (ai != null) {
                 return ai;
             }
@@ -539,10 +599,16 @@
     @Override
     public ServiceInfo getServiceInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        return getServiceInfo(className, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public ServiceInfo getServiceInfo(ComponentName className, ComponentInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             ServiceInfo si = mPM.getServiceInfo(className,
-                    updateFlagsForComponent(flags, userId, null), userId);
+                    updateFlagsForComponent(flags.getValue(), userId, null), userId);
             if (si != null) {
                 return si;
             }
@@ -556,10 +622,16 @@
     @Override
     public ProviderInfo getProviderInfo(ComponentName className, int flags)
             throws NameNotFoundException {
+        return getProviderInfo(className, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public ProviderInfo getProviderInfo(ComponentName className, ComponentInfoFlags flags)
+            throws NameNotFoundException {
         final int userId = getUserId();
         try {
             ProviderInfo pi = mPM.getProviderInfo(className,
-                    updateFlagsForComponent(flags, userId, null), userId);
+                    updateFlagsForComponent(flags.getValue(), userId, null), userId);
             if (pi != null) {
                 return pi;
             }
@@ -582,16 +654,30 @@
     /** @hide */
     @Override
     public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
+        return this.getSharedLibraries(PackageInfoFlags.of(flags));
+    }
+
+    /** @hide
+     * @param flags */
+    @Override
+    public @NonNull List<SharedLibraryInfo> getSharedLibraries(PackageInfoFlags flags) {
         return getSharedLibrariesAsUser(flags, getUserId());
     }
 
     /** @hide */
     @Override
-    @SuppressWarnings("unchecked")
     public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags, int userId) {
+        return getSharedLibrariesAsUser(PackageInfoFlags.of(flags), userId);
+    }
+
+    /** @hide */
+    @Override
+    @SuppressWarnings("unchecked")
+    public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(PackageInfoFlags flags,
+            int userId) {
         try {
             ParceledListSlice<SharedLibraryInfo> sharedLibs = mPM.getSharedLibraries(
-                    mContext.getOpPackageName(), flags, userId);
+                    mContext.getOpPackageName(), flags.getValue(), userId);
             if (sharedLibs == null) {
                 return Collections.emptyList();
             }
@@ -604,10 +690,18 @@
     @NonNull
     @Override
     public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
-            @InstallFlags int flags) {
+            int flags) {
+        return getDeclaredSharedLibraries(packageName, PackageInfoFlags.of(flags));
+    }
+
+    @NonNull
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
+            PackageInfoFlags flags) {
         try {
             ParceledListSlice<SharedLibraryInfo> sharedLibraries = mPM.getDeclaredSharedLibraries(
-                    packageName, flags, mContext.getUserId());
+                    packageName, flags.getValue(), mContext.getUserId());
             return sharedLibraries != null ? sharedLibraries.getList() : Collections.emptyList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1092,6 +1186,12 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<PackageInfo> getInstalledPackages(int flags) {
+        return getInstalledPackages(PackageInfoFlags.of(flags));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<PackageInfo> getInstalledPackages(PackageInfoFlags flags) {
         return getInstalledPackagesAsUser(flags, getUserId());
     }
 
@@ -1099,9 +1199,17 @@
     @Override
     @SuppressWarnings("unchecked")
     public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+        return getInstalledPackagesAsUser(PackageInfoFlags.of(flags), userId);
+    }
+
+    /** @hide */
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<PackageInfo> getInstalledPackagesAsUser(PackageInfoFlags flags, int userId) {
         try {
             ParceledListSlice<PackageInfo> parceledList =
-                    mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
+                    mPM.getInstalledPackages(updateFlagsForPackage(flags.getValue(), userId),
+                            userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1113,13 +1221,19 @@
 
     @SuppressWarnings("unchecked")
     @Override
-    public List<PackageInfo> getPackagesHoldingPermissions(
-            String[] permissions, int flags) {
+    public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
+        return this.getPackagesHoldingPermissions(permissions, PackageInfoFlags.of(flags));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions,
+            PackageInfoFlags flags) {
         final int userId = getUserId();
         try {
             ParceledListSlice<PackageInfo> parceledList =
                     mPM.getPackagesHoldingPermissions(permissions,
-                            updateFlagsForPackage(flags, userId), userId);
+                            updateFlagsForPackage(flags.getValue(), userId), userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1135,13 +1249,27 @@
         return getInstalledApplicationsAsUser(flags, getUserId());
     }
 
+    @Override
+    public List<ApplicationInfo> getInstalledApplications(ApplicationInfoFlags flags) {
+        return getInstalledApplicationsAsUser(flags, getUserId());
+    }
+
     /** @hide */
     @SuppressWarnings("unchecked")
     @Override
     public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
+        return getInstalledApplicationsAsUser(ApplicationInfoFlags.of(flags), userId);
+    }
+
+    /** @hide */
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<ApplicationInfo> getInstalledApplicationsAsUser(ApplicationInfoFlags flags,
+            int userId) {
         try {
             ParceledListSlice<ApplicationInfo> parceledList =
-                    mPM.getInstalledApplications(updateFlagsForApplication(flags, userId), userId);
+                    mPM.getInstalledApplications(updateFlagsForApplication(
+                            flags.getValue(), userId), userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1249,16 +1377,26 @@
 
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
+        return resolveActivity(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public ResolveInfo resolveActivity(Intent intent, ResolveInfoFlags flags) {
         return resolveActivityAsUser(intent, flags, getUserId());
     }
 
     @Override
     public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+        return resolveActivityAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    public ResolveInfo resolveActivityAsUser(Intent intent, ResolveInfoFlags flags, int userId) {
         try {
             return mPM.resolveIntent(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                updateFlagsForComponent(flags, userId, intent),
+                updateFlagsForComponent(flags.getValue(), userId, intent),
                 userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1266,21 +1404,31 @@
     }
 
     @Override
-    public List<ResolveInfo> queryIntentActivities(Intent intent,
-                                                   int flags) {
+    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        return queryIntentActivities(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentActivities(Intent intent, ResolveInfoFlags flags) {
         return queryIntentActivitiesAsUser(intent, flags, getUserId());
     }
 
     /** @hide Same as above but for a specific user */
     @Override
+    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
+        return queryIntentActivitiesAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    /** @hide Same as above but for a specific user */
+    @Override
     @SuppressWarnings("unchecked")
-    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
-            int flags, int userId) {
+    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags,
+            int userId) {
         try {
             ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivities(
                     intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    updateFlagsForComponent(flags, userId, intent),
+                    updateFlagsForComponent(flags.getValue(), userId, intent),
                     userId);
             if (parceledList == null) {
                 return Collections.emptyList();
@@ -1292,22 +1440,30 @@
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
             Intent intent, int flags) {
+        return queryIntentActivityOptions(caller,
+                specifics == null ? null : new ArrayList<>(Arrays.asList(specifics)),
+                intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+            List<Intent> specifics, Intent intent, ResolveInfoFlags flags) {
         final int userId = getUserId();
         final ContentResolver resolver = mContext.getContentResolver();
 
         String[] specificTypes = null;
         if (specifics != null) {
-            final int N = specifics.length;
-            for (int i=0; i<N; i++) {
-                Intent sp = specifics[i];
+            final int numSpecifics = specifics.size();
+            for (int i = 0; i < numSpecifics; i++) {
+                Intent sp = specifics.get(i);
                 if (sp != null) {
                     String t = sp.resolveTypeIfNeeded(resolver);
                     if (t != null) {
                         if (specificTypes == null) {
-                            specificTypes = new String[N];
+                            specificTypes = new String[numSpecifics];
                         }
                         specificTypes[i] = t;
                     }
@@ -1318,11 +1474,11 @@
         try {
             ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivityOptions(
                     caller,
-                    specifics,
+                    specifics == null ? null : specifics.toArray(new Intent[0]),
                     specificTypes,
                     intent,
                     intent.resolveTypeIfNeeded(resolver),
-                    updateFlagsForComponent(flags, userId, intent),
+                    updateFlagsForComponent(flags.getValue(), userId, intent),
                     userId);
             if (parceledList == null) {
                 return Collections.emptyList();
@@ -1337,13 +1493,22 @@
      * @hide
      */
     @Override
-    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
+        return queryBroadcastReceiversAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, ResolveInfoFlags flags,
+            int userId) {
         try {
             ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentReceivers(
                     intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    updateFlagsForComponent(flags, userId, intent),
+                    updateFlagsForComponent(flags.getValue(), userId, intent),
                     userId);
             if (parceledList == null) {
                 return Collections.emptyList();
@@ -1356,17 +1521,27 @@
 
     @Override
     public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+        return queryBroadcastReceivers(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, ResolveInfoFlags flags) {
         return queryBroadcastReceiversAsUser(intent, flags, getUserId());
     }
 
     @Override
-    public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, @UserIdInt int userId) {
+        return resolveServiceAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, ResolveInfoFlags flags,
             @UserIdInt int userId) {
         try {
             return mPM.resolveService(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                updateFlagsForComponent(flags, userId, intent),
+                updateFlagsForComponent(flags.getValue(), userId, intent),
                 userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1375,17 +1550,28 @@
 
     @Override
     public ResolveInfo resolveService(Intent intent, int flags) {
+        return resolveService(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public ResolveInfo resolveService(Intent intent, ResolveInfoFlags flags) {
         return resolveServiceAsUser(intent, flags, getUserId());
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+        return queryIntentServicesAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, ResolveInfoFlags flags,
+            int userId) {
         try {
             ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentServices(
                     intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    updateFlagsForComponent(flags, userId, intent),
+                    updateFlagsForComponent(flags.getValue(), userId, intent),
                     userId);
             if (parceledList == null) {
                 return Collections.emptyList();
@@ -1398,18 +1584,29 @@
 
     @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+        return queryIntentServices(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentServices(Intent intent, ResolveInfoFlags flags) {
         return queryIntentServicesAsUser(intent, flags, getUserId());
     }
 
     @Override
+    public List<ResolveInfo> queryIntentContentProvidersAsUser(
+            Intent intent, int flags, int userId) {
+        return queryIntentContentProvidersAsUser(intent, ResolveInfoFlags.of(flags), userId);
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentContentProvidersAsUser(
-            Intent intent, int flags, int userId) {
+            Intent intent, ResolveInfoFlags flags, int userId) {
         try {
             ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentContentProviders(
                     intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    updateFlagsForComponent(flags, userId, intent),
+                    updateFlagsForComponent(flags.getValue(), userId, intent),
                     userId);
             if (parceledList == null) {
                 return Collections.emptyList();
@@ -1422,39 +1619,68 @@
 
     @Override
     public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
+        return queryIntentContentProviders(intent, ResolveInfoFlags.of(flags));
+    }
+
+    @Override
+    public List<ResolveInfo> queryIntentContentProviders(Intent intent, ResolveInfoFlags flags) {
         return queryIntentContentProvidersAsUser(intent, flags, getUserId());
     }
 
     @Override
     public ProviderInfo resolveContentProvider(String name, int flags) {
+        return resolveContentProvider(name, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public ProviderInfo resolveContentProvider(String name, ComponentInfoFlags flags) {
         return resolveContentProviderAsUser(name, flags, getUserId());
     }
 
     /** @hide **/
     @Override
     public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) {
+        return resolveContentProviderAsUser(name, ComponentInfoFlags.of(flags), userId);
+    }
+
+    /** @hide **/
+    @Override
+    public ProviderInfo resolveContentProviderAsUser(String name, ComponentInfoFlags flags,
+            int userId) {
         try {
             return mPM.resolveContentProvider(name,
-                    updateFlagsForComponent(flags, userId, null), userId);
+                    updateFlagsForComponent(flags.getValue(), userId, null), userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
-    public List<ProviderInfo> queryContentProviders(String processName,
-            int uid, int flags) {
+    public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
+        return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags));
+    }
+
+    @Override
+    public List<ProviderInfo> queryContentProviders(String processName, int uid,
+            ComponentInfoFlags flags) {
         return queryContentProviders(processName, uid, flags, null);
     }
 
     @Override
+    public List<ProviderInfo> queryContentProviders(String processName,
+            int uid, int flags, String metaDataKey) {
+        return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags), metaDataKey);
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public List<ProviderInfo> queryContentProviders(String processName,
-            int uid, int flags, String metaDataKey) {
+            int uid, ComponentInfoFlags flags, String metaDataKey) {
         try {
             ParceledListSlice<ProviderInfo> slice = mPM.queryContentProviders(processName, uid,
-                    updateFlagsForComponent(flags, UserHandle.getUserId(uid), null), metaDataKey);
-            return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
+                    updateFlagsForComponent(flags.getValue(), UserHandle.getUserId(uid),
+                            null), metaDataKey);
+            return slice != null ? slice.getList() : Collections.emptyList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1839,7 +2065,7 @@
     /**
      * Update given flags when being used to request {@link PackageInfo}.
      */
-    private int updateFlagsForPackage(int flags, int userId) {
+    private long updateFlagsForPackage(long flags, int userId) {
         if ((flags & (GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS)) != 0) {
             // Caller is asking for component details, so they'd better be
             // asking for specific Direct Boot matching behavior
@@ -1855,14 +2081,15 @@
     /**
      * Update given flags when being used to request {@link ApplicationInfo}.
      */
-    private int updateFlagsForApplication(int flags, int userId) {
+    private long updateFlagsForApplication(long flags, int userId) {
         return updateFlagsForPackage(flags, userId);
     }
 
     /**
      * Update given flags when being used to request {@link ComponentInfo}.
      */
-    private int updateFlagsForComponent(int flags, int userId, Intent intent) {
+    private long updateFlagsForComponent(@ComponentInfoFlagsBits long flags, int userId,
+            Intent intent) {
         if (intent != null) {
             if ((intent.getFlags() & Intent.FLAG_DIRECT_BOOT_AUTO) != 0) {
                 flags |= MATCH_DIRECT_BOOT_AUTO;
@@ -2109,18 +2336,24 @@
     }
 
     @Nullable
+    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
+        return getPackageArchiveInfo(archiveFilePath, PackageInfoFlags.of(flags));
+    }
+
+    @Nullable
     public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
-            @PackageInfoFlags int flags) {
-        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+            PackageInfoFlags flags) {
+        long flagsBits = flags.getValue();
+        if ((flagsBits & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                 | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
             // Caller expressed no opinion about what encryption
             // aware/unaware components they want to see, so match both
-            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+            flagsBits |= PackageManager.MATCH_DIRECT_BOOT_AWARE
                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
         }
 
-        boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
-                || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
+        boolean collectCertificates = (flagsBits & PackageManager.GET_SIGNATURES) != 0
+                || (flagsBits & PackageManager.GET_SIGNING_CERTIFICATES) != 0;
 
         ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
         ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
@@ -2129,8 +2362,8 @@
         if (result.isError()) {
             return null;
         }
-        return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
-                FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
+        return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flagsBits, 0, 0,
+                null, FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
     }
 
     @Override
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 8637e31..58f60a6 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -154,9 +154,12 @@
         int configDiff;
         boolean equivalent;
 
+        // Get theme outside of synchronization to avoid nested lock.
+        final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
+        final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
+        final Resources.Theme systemUiTheme =
+                systemUiContext != null ? systemUiContext.getTheme() : null;
         synchronized (mResourcesManager) {
-            final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
-            final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme();
             if (mPendingConfiguration != null) {
                 if (!mPendingConfiguration.isOtherSeqNewer(config)) {
                     config = mPendingConfiguration;
@@ -207,7 +210,8 @@
                 systemTheme.rebase();
             }
 
-            if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
+            if (systemUiTheme != null
+                    && (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
                 systemUiTheme.rebase();
             }
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4a7361e..885feb1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3212,6 +3212,10 @@
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
         mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+        if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+                && mToken instanceof WindowTokenClient) {
+            mMainThread.onSystemUiContextCleanup(this);
+        }
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 2c0f983..bc78df5 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -39,7 +39,7 @@
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlagsBits;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -1296,7 +1296,7 @@
     @RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
     @SystemApi(client = Client.MODULE_LIBRARIES)
     @TestApi
-    public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
+    public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlagsBits int flags) {
         try {
             ParceledListSlice<ResolveInfo> parceledList = ActivityManager.getService()
                     .queryIntentComponentsForIntentSender(mTarget, flags);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 81e6ae4..5002a59 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -131,6 +131,7 @@
 import android.media.tv.tunerresourcemanager.ITunerResourceManager;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.net.ConnectivityFrameworkInitializer;
+import android.net.ConnectivityFrameworkInitializerTiramisu;
 import android.net.EthernetManager;
 import android.net.IEthernetManager;
 import android.net.IIpSecService;
@@ -146,8 +147,6 @@
 import android.net.VpnManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
 import android.net.vcn.IVcnManagementService;
 import android.net.vcn.VcnManager;
 import android.net.wifi.WifiFrameworkInitializer;
@@ -576,15 +575,6 @@
                     ctx.mMainThread.getHandler());
             }});
 
-        registerService(Context.NSD_SERVICE, NsdManager.class,
-                new CachedServiceFetcher<NsdManager>() {
-            @Override
-            public NsdManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getServiceOrThrow(Context.NSD_SERVICE);
-                INsdManager service = INsdManager.Stub.asInterface(b);
-                return new NsdManager(ctx.getOuterContext(), service);
-            }});
-
         registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
                 new CachedServiceFetcher<PeopleManager>() {
             @Override
@@ -1547,6 +1537,7 @@
             SupplementalProcessFrameworkInitializer.registerServiceWrappers();
             UwbFrameworkInitializer.registerServiceWrappers();
             SafetyCenterFrameworkInitializer.registerServiceWrappers();
+            ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
         } finally {
             // If any of the above code throws, we're in a pretty bad shape and the process
             // will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 20122fb..602a186 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2261,21 +2261,21 @@
     public @interface LeFeatureReturnValues {}
 
     /**
-     * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Connected Isochronous Stream Central
-     * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
+     * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is
+     * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if
      * the feature is not supported or an error code.
      *
-     * @return whether the chipset supports the LE Connected Isochronous Stream Central feature
+     * @return whether the LE audio is supported
      */
     @RequiresNoPermission
-    public @LeFeatureReturnValues int isCisCentralSupported() {
+    public @LeFeatureReturnValues int isLeAudioSupported() {
         if (!getLeAccess()) {
             return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
         }
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                return mService.isCisCentralSupported();
+                return mService.isLeAudioSupported();
             }
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 8212eaa..c046324 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -601,6 +601,49 @@
     }
 
     /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
+     * {@link BluetoothProfile#PRIORITY_OFF}
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresLegacyBluetoothAdminPermission
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+    })
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF
+                    && priority != BluetoothProfile.PRIORITY_ON) {
+                return false;
+            }
+            try {
+                return service.setPriority(
+                        device, BluetoothAdapter.priorityToConnectionPolicy(priority),
+                        mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
      * Set connection policy of the profile
      *
      * <p> The device should already be paired.
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2b12f12..ae13425 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -776,6 +776,51 @@
         }
     }
 
+    /**
+     * Notify the system that the given self-managed association has just 'appeared'.
+     * This causes the system to bind to the companion app to keep it running until the association
+     * is reported as 'disappeared'
+     *
+     * <p>This API is only available for the companion apps that manage the connectivity by
+     * themselves.</p>
+     *
+     * @param associationId the unique {@link AssociationInfo#getId ID} assigned to the Association
+     * recorded by CompanionDeviceManager
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED)
+    public void notifyDeviceAppeared(int associationId) {
+        try {
+            mService.notifyDeviceAppeared(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the system that the given self-managed association has just 'disappeared'.
+     * This causes the system to unbind to the companion app.
+     *
+     * <p>This API is only available for the companion apps that manage the connectivity by
+     * themselves.</p>
+     *
+     * @param associationId the unique {@link AssociationInfo#getId ID} assigned to the Association
+     * recorded by CompanionDeviceManager
+
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED)
+    public void notifyDeviceDisappeared(int associationId) {
+        try {
+            mService.notifyDeviceDisappeared(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private boolean checkFeaturePresent() {
         boolean featurePresent = mService != null;
         if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 15266d6..3237f7c 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -21,12 +21,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.Log;
 
+
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.Objects;
@@ -34,8 +36,9 @@
 /**
  * Service to be implemented by apps that manage a companion device.
  *
- * System will keep this service bound whenever an associated device is nearby,
- * ensuring app stays alive.
+ * System will keep this service bound whenever an associated device is nearby for Bluetooth
+ * devices or companion app manages the connectivity and reports disappeared, ensuring app stays
+ * alive
  *
  * An app must be {@link CompanionDeviceManager#associate associated} with at leas one device,
  * before it can take advantage of this service.
@@ -43,6 +46,17 @@
  * You must declare this service in your manifest with an
  * intent-filter action of {@link #SERVICE_INTERFACE} and
  * permission of {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}
+ *
+ * <p>If you want to declare more than one of these services, you must declare the meta-data in the
+ * service of your manifest with the corresponding name and value to true to indicate the
+ * primary service.
+ * Only the primary one will get the callback from
+ * {@link #onDeviceAppeared(AssociationInfo associationInfo)}.</p>
+ *
+ * Example:
+ * <meta-data
+ *   android:name="primary"
+ *   android:value="true" />
  */
 public abstract class CompanionDeviceService extends Service {
 
@@ -52,13 +66,14 @@
      * An intent action for a service to be bound whenever this app's companion device(s)
      * are nearby.
      *
-     * <p>The app will be kept alive for as long as the device is nearby.
+     * <p>The app will be kept alive for as long as the device is nearby or companion app reports
+     * appeared.
      * If the app is not running at the time device gets connected, the app will be woken up.</p>
      *
-     * <p>Shortly after the device goes out of range, the service will be unbound, and the
-     * app will be eligible for cleanup, unless any other user-visible components are running.</p>
+     * <p>Shortly after the device goes out of range or the companion app reports disappeared,
+     * the service will be unbound, and the app will be eligible for cleanup, unless any other
+     * user-visible components are running.</p>
      *
-     * <p>An app shouldn't declare more than one of these services.
      * If running in background is not essential for the devices that this app can manage,
      * app should avoid declaring this service.</p>
      *
@@ -73,9 +88,13 @@
      * Called by system whenever a device associated with this app is available.
      *
      * @param address the MAC address of the device
+     * @deprecated please override {@link #onDeviceAppeared(AssociationInfo)} instead.
      */
+    @Deprecated
     @MainThread
-    public abstract void onDeviceAppeared(@NonNull String address);
+    public void onDeviceAppeared(@NonNull String address) {
+        // Do nothing. Companion apps can override this function.
+    }
 
     /**
      * Called by system whenever a device associated with this app stops being available.
@@ -83,9 +102,13 @@
      * Usually this means the device goes out of range or is turned off.
      *
      * @param address the MAC address of the device
+     * @deprecated please override {@link #onDeviceDisappeared(AssociationInfo)} instead.
      */
+    @Deprecated
     @MainThread
-    public abstract void onDeviceDisappeared(@NonNull String address);
+    public void onDeviceDisappeared(@NonNull String address) {
+        // Do nothing. Companion apps can override this function.
+    }
 
     /**
      * Called by system whenever the system dispatches a message to the app to send it to
@@ -118,10 +141,35 @@
         companionDeviceManager.dispatchMessage(messageId, associationId, message);
     }
 
+    /**
+     * Called by system whenever a device associated with this app is connected.
+     *
+     * @param associationInfo A record for the companion device.
+     */
+    @MainThread
+    public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) {
+        if (!associationInfo.isSelfManaged()) {
+            onDeviceAppeared(associationInfo.getDeviceMacAddressAsString());
+        }
+    }
+
+    /**
+     * Called by system whenever a device associated with this app is disconnected.
+     *
+     * @param associationInfo A record for the companion device.
+     */
+    @MainThread
+    public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
+        if (!associationInfo.isSelfManaged()) {
+            onDeviceDisappeared(associationInfo.getDeviceMacAddressAsString());
+        }
+    }
+
     @Nullable
     @Override
     public final IBinder onBind(@NonNull Intent intent) {
         if (Objects.equals(intent.getAction(), SERVICE_INTERFACE)) {
+            onBindCompanionDeviceService(intent);
             return mRemote;
         }
         Log.w(LOG_TAG,
@@ -129,20 +177,26 @@
         return null;
     }
 
+    /**
+     * Used to track the state of Binder connection in CTS tests.
+     * @hide
+     */
+    @TestApi
+    public void onBindCompanionDeviceService(@NonNull Intent intent) {
+    }
+
     class Stub extends ICompanionDeviceService.Stub {
 
         @Override
-        public void onDeviceAppeared(String address) {
-            Handler.getMain().sendMessage(PooledLambda.obtainMessage(
-                    CompanionDeviceService::onDeviceAppeared,
-                    CompanionDeviceService.this, address));
+        public void onDeviceAppeared(AssociationInfo associationInfo) {
+            Handler.getMain().post(
+                    () -> CompanionDeviceService.this.onDeviceAppeared(associationInfo));
         }
 
         @Override
-        public void onDeviceDisappeared(String address) {
-            Handler.getMain().sendMessage(PooledLambda.obtainMessage(
-                    CompanionDeviceService::onDeviceDisappeared,
-                    CompanionDeviceService.this, address));
+        public void onDeviceDisappeared(AssociationInfo associationInfo) {
+            Handler.getMain().post(
+                    () -> CompanionDeviceService.this.onDeviceDisappeared(associationInfo));
         }
 
         public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) {
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 1558db2..68a6031f 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -65,5 +65,10 @@
     void dispatchMessage(in int messageId, in int associationId, in byte[] message);
 
     void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
+
     void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
+
+    void notifyDeviceAppeared(int associationId);
+
+    void notifyDeviceDisappeared(int associationId);
 }
diff --git a/core/java/android/companion/ICompanionDeviceService.aidl b/core/java/android/companion/ICompanionDeviceService.aidl
index 25212a1..4e453573 100644
--- a/core/java/android/companion/ICompanionDeviceService.aidl
+++ b/core/java/android/companion/ICompanionDeviceService.aidl
@@ -16,9 +16,11 @@
 
 package android.companion;
 
+import android.companion.AssociationInfo;
+
 /** @hide */
 oneway interface ICompanionDeviceService {
-    void onDeviceAppeared(in String address);
-    void onDeviceDisappeared(in String address);
+    void onDeviceAppeared(in AssociationInfo associationInfo);
+    void onDeviceDisappeared(in AssociationInfo associationInfo);
     void onDispatchMessage(in int messageId, in int associationId, in byte[] message);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index dabc603..82ad150 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -16,6 +16,13 @@
 
 package android.companion.virtual;
 
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+
 /**
  * Interface for a virtual device.
  *
@@ -34,4 +41,29 @@
      * Closes the virtual device and frees all associated resources.
      */
     void close();
+    void createVirtualKeyboard(
+            int displayId,
+            String inputDeviceName,
+            int vendorId,
+            int productId,
+            IBinder token);
+    void createVirtualMouse(
+            int displayId,
+            String inputDeviceName,
+            int vendorId,
+            int productId,
+            IBinder token);
+    void createVirtualTouchscreen(
+            int displayId,
+            String inputDeviceName,
+            int vendorId,
+            int productId,
+            IBinder token,
+            in Point screenSize);
+    void unregisterInputDevice(IBinder token);
+    boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
+    boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
+    boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
+    boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
+    boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 590b108..0d024b1 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -23,7 +23,13 @@
 import android.annotation.SystemService;
 import android.companion.AssociationInfo;
 import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.VirtualDisplay;
+import android.hardware.input.VirtualKeyboard;
+import android.hardware.input.VirtualMouse;
+import android.hardware.input.VirtualTouchscreen;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 
 /**
@@ -73,6 +79,8 @@
      * A virtual device has its own virtual display, audio output, microphone, and camera etc. The
      * creator of a virtual device can take the output from the virtual display and stream it over
      * to another device, and inject input events that are received from the remote device.
+     *
+     * TODO(b/204081582): Consider using a builder pattern for the input APIs.
      */
     public static class VirtualDevice implements AutoCloseable {
 
@@ -95,5 +103,88 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * Creates a virtual keyboard.
+         *
+         * @param display the display that the events inputted through this device should target
+         * @param inputDeviceName the name to call this input device
+         * @param vendorId the vendor id
+         * @param productId the product id
+         * @hide
+         */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        @NonNull
+        public VirtualKeyboard createVirtualKeyboard(
+                @NonNull VirtualDisplay display,
+                @NonNull String inputDeviceName,
+                int vendorId,
+                int productId) {
+            try {
+                final IBinder token = new Binder(
+                        "android.hardware.input.VirtualKeyboard:" + inputDeviceName);
+                mVirtualDevice.createVirtualKeyboard(display.getDisplay().getDisplayId(),
+                        inputDeviceName, vendorId, productId, token);
+                return new VirtualKeyboard(mVirtualDevice, token);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Creates a virtual mouse.
+         *
+         * @param display the display that the events inputted through this device should target
+         * @param inputDeviceName the name to call this input device
+         * @param vendorId the vendor id
+         * @param productId the product id
+         * @hide
+         */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        @NonNull
+        public VirtualMouse createVirtualMouse(
+                @NonNull VirtualDisplay display,
+                @NonNull String inputDeviceName,
+                int vendorId,
+                int productId) {
+            try {
+                final IBinder token = new Binder(
+                        "android.hardware.input.VirtualMouse:" + inputDeviceName);
+                mVirtualDevice.createVirtualMouse(display.getDisplay().getDisplayId(),
+                        inputDeviceName, vendorId, productId, token);
+                return new VirtualMouse(mVirtualDevice, token);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Creates a virtual touchscreen.
+         *
+         * @param display the display that the events inputted through this device should target
+         * @param inputDeviceName the name to call this input device
+         * @param vendorId the vendor id
+         * @param productId the product id
+         * @hide
+         */
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        @NonNull
+        public VirtualTouchscreen createVirtualTouchscreen(
+                @NonNull VirtualDisplay display,
+                @NonNull String inputDeviceName,
+                int vendorId,
+                int productId) {
+            try {
+                final IBinder token = new Binder(
+                        "android.hardware.input.VirtualTouchscreen:" + inputDeviceName);
+                final Point size = new Point();
+                display.getDisplay().getSize(size);
+                mVirtualDevice.createVirtualTouchscreen(display.getDisplay().getDisplayId(),
+                        inputDeviceName, vendorId, productId, token, size);
+                return new VirtualTouchscreen(mVirtualDevice, token);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 18237a2..983d0cc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -9236,7 +9236,7 @@
      * @see #resolveActivity
      */
     public ActivityInfo resolveActivityInfo(@NonNull PackageManager pm,
-            @PackageManager.ComponentInfoFlags int flags) {
+            @PackageManager.ComponentInfoFlagsBits int flags) {
         ActivityInfo ai = null;
         if (mComponent != null) {
             try {
@@ -9264,7 +9264,7 @@
      */
     @UnsupportedAppUsage
     public @Nullable ComponentName resolveSystemService(@NonNull PackageManager pm,
-            @PackageManager.ComponentInfoFlags int flags) {
+            @PackageManager.ComponentInfoFlagsBits int flags) {
         if (mComponent != null) {
             return mComponent;
         }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ffe4ea4..8bea006 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -304,12 +304,23 @@
      * @see android.R.attr#colorMode
      */
     public static final int COLOR_MODE_HDR = 2;
+    // 3 Corresponds to android::uirenderer::ColorMode::Hdr10.
+    /**
+     * Value of {@link #colorMode} indicating that the activity should use an
+     * 8 bit alpha buffer if the presentation display supports it.
+     *
+     * @see android.R.attr#colorMode
+     * @hide
+     */
+    public static final int COLOR_MODE_A8 = 4;
+
 
     /** @hide */
     @IntDef(prefix = { "COLOR_MODE_" }, value = {
             COLOR_MODE_DEFAULT,
             COLOR_MODE_WIDE_COLOR_GAMUT,
             COLOR_MODE_HDR,
+            COLOR_MODE_A8,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ColorMode {}
@@ -1682,6 +1693,8 @@
                 return "COLOR_MODE_WIDE_COLOR_GAMUT";
             case COLOR_MODE_HDR:
                 return "COLOR_MODE_HDR";
+            case COLOR_MODE_A8:
+                return "COLOR_MODE_A8";
             default:
                 return Integer.toString(colorMode);
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 84c9fa9..2998f76 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1148,6 +1148,12 @@
     public int versionCode;
 
     /**
+     * The timestamp of when this ApplicationInfo was created.
+     * @hide
+     */
+    public long createTimestamp;
+
+    /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
      * to have been compiled, or {@code 0} if not specified.
      * <p>
@@ -1639,6 +1645,7 @@
                         + requestRawExternalStorageAccess);
             }
         }
+        pw.println(prefix + "createTimestamp=" + createTimestamp);
         super.dumpBack(pw, prefix);
     }
 
@@ -1796,6 +1803,7 @@
     }
 
     public ApplicationInfo() {
+        createTimestamp = System.currentTimeMillis();
     }
     
     public ApplicationInfo(ApplicationInfo orig) {
@@ -1867,6 +1875,7 @@
         memtagMode = orig.memtagMode;
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
+        createTimestamp = System.currentTimeMillis();
     }
 
     public String toString() {
@@ -1957,6 +1966,7 @@
         dest.writeInt(memtagMode);
         dest.writeInt(nativeHeapZeroInitialized);
         sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
+        dest.writeLong(createTimestamp);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2044,6 +2054,7 @@
         memtagMode = source.readInt();
         nativeHeapZeroInitialized = source.readInt();
         requestRawExternalStorageAccess = sForBoolean.unparcel(source);
+        createTimestamp = source.readLong();
     }
 
     /**
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index ae5f71bc..617d3ab 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -41,7 +41,7 @@
 import android.content.pm.PackageInstaller.SessionCallback;
 import android.content.pm.PackageInstaller.SessionCallbackDelegate;
 import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.ApplicationInfoFlagsBits;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -1012,7 +1012,7 @@
      *         isn't enabled.
      */
     public ApplicationInfo getApplicationInfo(@NonNull String packageName,
-            @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+            @ApplicationInfoFlagsBits int flags, @NonNull UserHandle user)
             throws PackageManager.NameNotFoundException {
         Objects.requireNonNull(packageName, "packageName");
         Objects.requireNonNull(user, "user");
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1c35b47..f984f43 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -22,6 +22,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -655,7 +656,7 @@
      */
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+    @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
             GET_ACTIVITIES,
             GET_CONFIGURATIONS,
             GET_GIDS,
@@ -685,10 +686,10 @@
             GET_ATTRIBUTIONS,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PackageInfoFlags {}
+    public @interface PackageInfoFlagsBits {}
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+    @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
             GET_META_DATA,
             GET_SHARED_LIBRARY_FILES,
             MATCH_UNINSTALLED_PACKAGES,
@@ -704,10 +705,10 @@
             MATCH_APEX,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ApplicationInfoFlags {}
+    public @interface ApplicationInfoFlagsBits {}
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+    @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
             GET_META_DATA,
             GET_SHARED_LIBRARY_FILES,
             MATCH_ALL,
@@ -727,10 +728,10 @@
             GET_UNINSTALLED_PACKAGES,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ComponentInfoFlags {}
+    public @interface ComponentInfoFlagsBits {}
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+    @LongDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
             GET_META_DATA,
             GET_RESOLVED_FILTER,
             GET_SHARED_LIBRARY_FILES,
@@ -750,7 +751,7 @@
             GET_UNINSTALLED_PACKAGES,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ResolveInfoFlags {}
+    public @interface ResolveInfoFlagsBits {}
 
     /** @hide */
     @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
@@ -4661,6 +4662,74 @@
      */
     public static final String PROPERTY_ALLOW_ADB_BACKUP = "android.backup.ALLOW_ADB_BACKUP";
 
+    /**
+     * Flags class that wraps around the bitmask flags used in methods that retrieve package or
+     * application info.
+     * @hide
+     */
+    public static class Flags {
+        final long mValue;
+        protected Flags(long value) {
+            mValue = value;
+        }
+        public long getValue() {
+            return mValue;
+        }
+    }
+
+    /**
+     * Specific flags used for retrieving package info. Example:
+     * {@code PackageManager.getPackageInfo(packageName, PackageInfoFlags.of(0)}
+     */
+    public final static class PackageInfoFlags extends Flags {
+        private PackageInfoFlags(@PackageInfoFlagsBits long value) {
+            super(value);
+        }
+        @NonNull
+        public static PackageInfoFlags of(@PackageInfoFlagsBits long value) {
+            return new PackageInfoFlags(value);
+        }
+    }
+
+    /**
+     * Specific flags used for retrieving application info.
+     */
+    public final static class ApplicationInfoFlags extends Flags {
+        private ApplicationInfoFlags(@ApplicationInfoFlagsBits long value) {
+            super(value);
+        }
+        @NonNull
+        public static ApplicationInfoFlags of(@ApplicationInfoFlagsBits long value) {
+            return new ApplicationInfoFlags(value);
+        }
+    }
+
+    /**
+     * Specific flags used for retrieving component info.
+     */
+    public final static class ComponentInfoFlags extends Flags {
+        private ComponentInfoFlags(@ComponentInfoFlagsBits long value) {
+            super(value);
+        }
+        @NonNull
+        public static ComponentInfoFlags of(@ComponentInfoFlagsBits long value) {
+            return new ComponentInfoFlags(value);
+        }
+    }
+
+    /**
+     * Specific flags used for retrieving resolve info.
+     */
+    public final static class ResolveInfoFlags extends Flags {
+        private ResolveInfoFlags(@ResolveInfoFlagsBits long value) {
+            super(value);
+        }
+        @NonNull
+        public static ResolveInfoFlags of(@ResolveInfoFlagsBits long value) {
+            return new ResolveInfoFlags(value);
+        }
+    }
+
     /** {@hide} */
     public int getUserId() {
         return UserHandle.myUserId();
@@ -4689,12 +4758,23 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getPackageInfo(String, PackageInfoFlags)} instead.
      */
-    public abstract PackageInfo getPackageInfo(@NonNull String packageName,
-            @PackageInfoFlags int flags)
+    @Deprecated
+    public abstract PackageInfo getPackageInfo(@NonNull String packageName, int flags)
             throws NameNotFoundException;
 
     /**
+     * See {@link #getPackageInfo(String, int)}
+     */
+    @NonNull
+    public PackageInfo getPackageInfo(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageInfo not implemented in subclass");
+    }
+
+    /**
      * Retrieve overall information about an application package that is
      * installed on the system. This method can be used for retrieving
      * information about packages for which multiple versions can be installed
@@ -4715,9 +4795,21 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getPackageInfo(VersionedPackage, PackageInfoFlags)} instead.
      */
+    @Deprecated
     public abstract PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
-            @PackageInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
+
+    /**
+     * See {@link #getPackageInfo(VersionedPackage, int)}
+     */
+    @NonNull
+    public PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage,
+            @NonNull PackageInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageInfo not implemented in subclass");
+    }
 
     /**
      * Retrieve overall information about an application package that is
@@ -4736,13 +4828,27 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getPackageInfoAsUser(String, PackageInfoFlags, int)} instead.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @UnsupportedAppUsage
     public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName,
-            @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+            int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * See {@link #getPackageInfoAsUser(String, int, int)}
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @NonNull
+    public PackageInfo getPackageInfoAsUser(@NonNull String packageName,
+            @NonNull PackageInfoFlags flags, @UserIdInt int userId) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageInfoAsUser not implemented in subclass");
+    }
 
     /**
      * Map from the current package names in use on the device to whatever
@@ -4865,11 +4971,23 @@
      *         none.
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getPackageGids(String, PackageInfoFlags)} instead.
      */
-    public abstract int[] getPackageGids(@NonNull String packageName, @PackageInfoFlags int flags)
+    @Deprecated
+    public abstract int[] getPackageGids(@NonNull String packageName, int flags)
             throws NameNotFoundException;
 
     /**
+     * See {@link #getPackageGids(String, int)}.
+     */
+    @Nullable
+    public int[] getPackageGids(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageGids not implemented in subclass");
+    }
+
+    /**
      * Return the UID associated with the given package name.
      * <p>
      * Note that the same package will have different UIDs under different
@@ -4880,11 +4998,22 @@
      * @return Returns an integer UID who owns the given package name.
      * @throws NameNotFoundException if a package with the given name can not be
      *             found on the system.
+     * @deprecated Use {@link #getPackageUid(String, PackageInfoFlags)} instead.
      */
-    public abstract int getPackageUid(@NonNull String packageName, @PackageInfoFlags int flags)
+    @Deprecated
+    public abstract int getPackageUid(@NonNull String packageName, int flags)
             throws NameNotFoundException;
 
     /**
+     * See {@link #getPackageUid(String, int)}.
+     */
+    public int getPackageUid(@NonNull String packageName, @NonNull PackageInfoFlags flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageUid not implemented in subclass");
+    }
+
+    /**
      * Return the UID associated with the given package name.
      * <p>
      * Note that the same package will have different UIDs under different
@@ -4915,12 +5044,24 @@
      * @return Returns an integer UID who owns the given package name.
      * @throws NameNotFoundException if a package with the given name can not be
      *             found on the system.
+     * @deprecated Use {@link #getPackageUidAsUser(String, PackageInfoFlags, int)} instead.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
     public abstract int getPackageUidAsUser(@NonNull String packageName,
-            @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+            int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
+     * See {@link #getPackageUidAsUser(String, int, int)}.
+     * @hide
+     */
+    public int getPackageUidAsUser(@NonNull String packageName, @NonNull PackageInfoFlags flags,
+            @UserIdInt int userId) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getPackageUidAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all of the information we know about a particular permission.
@@ -5046,17 +5187,42 @@
      *         which had been deleted with {@code DELETE_KEEP_DATA} flag set).
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getApplicationInfo(String, ApplicationInfoFlags)} instead.
      */
     @NonNull
+    @Deprecated
     public abstract ApplicationInfo getApplicationInfo(@NonNull String packageName,
-            @ApplicationInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
 
-    /** {@hide} */
+    /**
+     * See {@link #getApplicationInfo(String, int)}.
+     */
+    @NonNull
+    public ApplicationInfo getApplicationInfo(@NonNull String packageName,
+            @NonNull ApplicationInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getApplicationInfo not implemented in subclass");
+    }
+
+    /**
+     * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead.
+     * {@hide}
+     */
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
+    @Deprecated
     public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
-            @ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
+            int flags, @UserIdInt int userId) throws NameNotFoundException;
+
+    /** {@hide} */
+    @NonNull
+    public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+            @NonNull ApplicationInfoFlags flags, @UserIdInt int userId)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getApplicationInfoAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all of the information we know about a particular
@@ -5074,13 +5240,29 @@
      *         which had been deleted with {@code DELETE_KEEP_DATA} flag set).
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, UserHandle)}
+     * instead.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    @Deprecated
+    public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
+            int flags, @NonNull UserHandle user)
+            throws NameNotFoundException {
+        return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
+    }
+
+    /**
+     * See {@link #getApplicationInfoAsUser(String, int, UserHandle)}.
      * @hide
      */
     @NonNull
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
     public ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName,
-            @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+            @NonNull ApplicationInfoFlags flags, @NonNull UserHandle user)
             throws NameNotFoundException {
         return getApplicationInfoAsUser(packageName, flags, user.getIdentifier());
     }
@@ -5106,10 +5288,22 @@
      *         activity.
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getActivityInfo(ComponentName, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract ActivityInfo getActivityInfo(@NonNull ComponentName component,
-            @ComponentInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
+
+    /**
+     * See {@link #getActivityInfo(ComponentName, int)}.
+     */
+    @NonNull
+    public ActivityInfo getActivityInfo(@NonNull ComponentName component,
+            @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getActivityInfo not implemented in subclass");
+    }
 
     /**
      * Retrieve all of the information we know about a particular receiver
@@ -5123,10 +5317,22 @@
      *         receiver.
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getReceiverInfo(ComponentName, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract ActivityInfo getReceiverInfo(@NonNull ComponentName component,
-            @ComponentInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
+
+    /**
+     * See {@link #getReceiverInfo(ComponentName, int)}.
+     */
+    @NonNull
+    public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
+            @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getReceiverInfo not implemented in subclass");
+    }
 
     /**
      * Retrieve all of the information we know about a particular service class.
@@ -5138,10 +5344,22 @@
      * @return A {@link ServiceInfo} object containing information about the
      *         service.
      * @throws NameNotFoundException if the component cannot be found on the system.
+     * @deprecated Use {@link #getServiceInfo(ComponentName, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component,
-            @ComponentInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
+
+    /**
+     * See {@link #getServiceInfo(ComponentName, int)}.
+     */
+    @NonNull
+    public ServiceInfo getServiceInfo(@NonNull ComponentName component,
+            @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getServiceInfo not implemented in subclass");
+    }
 
     /**
      * Retrieve all of the information we know about a particular content
@@ -5155,10 +5373,22 @@
      *         provider.
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
+     * @deprecated Use {@link #getProviderInfo(ComponentName, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @ComponentInfoFlags int flags) throws NameNotFoundException;
+            int flags) throws NameNotFoundException;
+
+    /**
+     * See {@link #getProviderInfo(ComponentName, int)}.
+     */
+    @NonNull
+    public ProviderInfo getProviderInfo(@NonNull ComponentName component,
+            @NonNull ComponentInfoFlags flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getProviderInfo not implemented in subclass");
+    }
 
     /**
      * Retrieve information for a particular module.
@@ -5203,9 +5433,21 @@
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @deprecated Use {@link #getInstalledPackages(PackageInfoFlags)} instead.
+     */
+    @Deprecated
+    @NonNull
+    public abstract List<PackageInfo> getInstalledPackages(int flags);
+
+    /**
+     * See {@link #getInstalledPackages(int)}.
+     * @param flags
      */
     @NonNull
-    public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
+    public List<PackageInfo> getInstalledPackages(@NonNull PackageInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "getInstalledPackages not implemented in subclass");
+    }
 
     /**
      * Return a List of all installed packages that are currently holding any of
@@ -5221,10 +5463,22 @@
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @deprecated Use {@link #getPackagesHoldingPermissions(String[], PackageInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract List<PackageInfo> getPackagesHoldingPermissions(
-            @NonNull String[] permissions, @PackageInfoFlags int flags);
+            @NonNull String[] permissions, int flags);
+
+    /**
+     * See {@link #getPackagesHoldingPermissions(String[], int)}.
+     */
+    @NonNull
+    public List<PackageInfo> getPackagesHoldingPermissions(
+            @NonNull String[] permissions, @NonNull PackageInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "getPackagesHoldingPermissions not implemented in subclass");
+    }
 
     /**
      * Return a List of all packages that are installed on the device, for a
@@ -5240,16 +5494,31 @@
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @deprecated Use {@link #getInstalledPackagesAsUser(PackageInfoFlags, int)} instead.
      * @hide
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-    public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
+    public abstract List<PackageInfo> getInstalledPackagesAsUser(int flags,
             @UserIdInt int userId);
 
     /**
+     * See {@link #getInstalledPackagesAsUser(int, int)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    public List<PackageInfo> getInstalledPackagesAsUser(@NonNull PackageInfoFlags flags,
+            @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "getApplicationInfoAsUser not implemented in subclass");
+    }
+
+    /**
      * Check whether a particular package has been granted a particular
      * permission.
      *
@@ -5935,11 +6204,22 @@
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @deprecated  Use {@link #getInstalledApplications(ApplicationInfoFlags)} instead.
      */
     @NonNull
-    public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
+    @Deprecated
+    public abstract List<ApplicationInfo> getInstalledApplications(int flags);
 
     /**
+     * See {@link #getInstalledApplications(int)}
+     * @param flags
+     */
+    @NonNull
+    public List<ApplicationInfo> getInstalledApplications(@NonNull ApplicationInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "getInstalledApplications not implemented in subclass");
+    }
+    /**
      * Return a List of all application packages that are installed on the
      * device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
      * set, a list of all applications including those deleted with
@@ -5957,13 +6237,27 @@
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
+     * @deprecated  Use {@link #getInstalledApplicationsAsUser(ApplicationInfoFlags, int)} instead.
      * @hide
      */
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @TestApi
+    @Deprecated
     public abstract List<ApplicationInfo> getInstalledApplicationsAsUser(
-            @ApplicationInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #getInstalledApplicationsAsUser(int, int}.
+     * @hide
+     */
+    @NonNull
+    @TestApi
+    public List<ApplicationInfo> getInstalledApplicationsAsUser(
+            @NonNull ApplicationInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "getInstalledApplicationsAsUser not implemented in subclass");
+    }
 
     /**
      * Gets the instant applications the user recently used.
@@ -6112,9 +6406,19 @@
      * @return The shared library list.
      *
      * @see #MATCH_UNINSTALLED_PACKAGES
+     * @deprecated Use {@link #getSharedLibraries(PackageInfoFlags)} instead.
      */
-    public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(
-            @InstallFlags int flags);
+    @Deprecated
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags);
+
+    /**
+     * See {@link #getSharedLibraries(int)}.
+     * @param flags
+     */
+    public @NonNull List<SharedLibraryInfo> getSharedLibraries(@NonNull PackageInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "getSharedLibraries() not implemented in subclass");
+    }
 
     /**
      * Get a list of shared libraries on the device.
@@ -6129,10 +6433,22 @@
      * @see #MATCH_UNINSTALLED_PACKAGES
      *
      * @hide
+     * @deprecated Use {@link #getSharedLibrariesAsUser(PackageInfoFlags, int)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
-    public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
-            @InstallFlags int flags, @UserIdInt int userId);
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags,
+            @UserIdInt int userId);
+
+    /**
+     * See {@link #getSharedLibrariesAsUser(int, int)}.
+     * @hide
+     */
+    public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
+            @NonNull PackageInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "getSharedLibrariesAsUser() not implemented in subclass");
+    }
 
     /**
      * Get the list of shared libraries declared by a package.
@@ -6142,13 +6458,28 @@
      * @return the shared library list
      *
      * @hide
+     * @deprecated Use {@link #getDeclaredSharedLibraries(String, PackageInfoFlags)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
     @SystemApi
     public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
-            @InstallFlags int flags) {
+            int flags) {
+        throw new UnsupportedOperationException(
+                "getDeclaredSharedLibraries() not implemented in subclass");
+    }
+
+    /**
+     * See {@link #getDeclaredSharedLibraries(String, int)}.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES)
+    @SystemApi
+    public List<SharedLibraryInfo> getDeclaredSharedLibraries(@NonNull String packageName,
+            @NonNull PackageInfoFlags flags) {
         throw new UnsupportedOperationException(
                 "getDeclaredSharedLibraries() not implemented in subclass");
     }
@@ -6249,10 +6580,20 @@
      *         matching activity was found. If multiple matching activities are
      *         found and there is no default set, returns a ResolveInfo object
      *         containing something else, such as the activity resolver.
+     * @deprecated Use {@link #resolveActivity(Intent, ResolveInfoFlags)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public abstract ResolveInfo resolveActivity(@NonNull Intent intent, int flags);
+
+    /**
+     * See {@link #resolveActivity(Intent, int)}.
      */
     @Nullable
-    public abstract ResolveInfo resolveActivity(@NonNull Intent intent,
-            @ResolveInfoFlags int flags);
+    public ResolveInfo resolveActivity(@NonNull Intent intent, @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "resolveActivity not implemented in subclass");
+    }
 
     /**
      * Determine the best action to perform for a given Intent for a given user.
@@ -6281,12 +6622,25 @@
      *         found and there is no default set, returns a ResolveInfo object
      *         containing something else, such as the activity resolver.
      * @hide
+     * @deprecated Use {@link #resolveActivityAsUser(Intent, ResolveInfoFlags, int)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #resolveActivityAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @Nullable
+    public ResolveInfo resolveActivityAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "resolveActivityAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all activities that can be performed for the given intent.
@@ -6302,10 +6656,21 @@
      *         words, the first item is what would be returned by
      *         {@link #resolveActivity}. If there are no matching activities, an
      *         empty list is returned.
+     * @deprecated Use {@link #queryIntentActivities(Intent, ResolveInfoFlags)} instead.
+     */
+    @Deprecated
+    @NonNull
+    public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent, int flags);
+
+    /**
+     * See {@link #queryIntentActivities(Intent, int)}.
      */
     @NonNull
-    public abstract List<ResolveInfo> queryIntentActivities(@NonNull Intent intent,
-            @ResolveInfoFlags int flags);
+    public List<ResolveInfo> queryIntentActivities(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryIntentActivities not implemented in subclass");
+    }
 
     /**
      * Retrieve all activities that can be performed for the given intent, for a
@@ -6323,12 +6688,25 @@
      *         {@link #resolveActivity}. If there are no matching activities, an
      *         empty list is returned.
      * @hide
+     * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, int)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #queryIntentActivitiesAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @NonNull
+    public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "queryIntentActivitiesAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all activities that can be performed for the given intent, for a
@@ -6347,13 +6725,28 @@
      *         {@link #resolveActivity}. If there are no matching activities, an
      *         empty list is returned.
      * @hide
+     * @deprecated Use {@link #queryIntentActivitiesAsUser(Intent, ResolveInfoFlags, UserHandle)}
+     * instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
     public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+            int flags, @NonNull UserHandle user) {
+        return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * See {@link #queryIntentActivitiesAsUser(Intent, int, UserHandle)}.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentActivitiesAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
         return queryIntentActivitiesAsUser(intent, flags, user.getIdentifier());
     }
 
@@ -6381,10 +6774,24 @@
      *         activities that can handle <var>intent</var> but did not get
      *         included by one of the <var>specifics</var> intents. If there are
      *         no matching activities, an empty list is returned.
+     * @deprecated Use {@link #queryIntentActivityOptions(ComponentName, List, Intent,
+     * ResolveInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
-            @Nullable Intent[] specifics, @NonNull Intent intent, @ResolveInfoFlags int flags);
+            @Nullable Intent[] specifics, @NonNull Intent intent, int flags);
+
+    /**
+     * See {@link #queryIntentActivityOptions(ComponentName, Intent[], Intent, int)}.
+     */
+    @NonNull
+    public List<ResolveInfo> queryIntentActivityOptions(@Nullable ComponentName caller,
+            @Nullable List<Intent> specifics, @NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryIntentActivityOptions not implemented in subclass");
+    }
 
     /**
      * Retrieve all receivers that can handle a broadcast of the given intent.
@@ -6394,10 +6801,21 @@
      * @return Returns a List of ResolveInfo objects containing one entry for
      *         each matching receiver, ordered from best to worst. If there are
      *         no matching receivers, an empty list or null is returned.
+     * @deprecated Use {@link #queryBroadcastReceivers(Intent, ResolveInfoFlags)} instead.
+     */
+    @Deprecated
+    @NonNull
+    public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent, int flags);
+
+    /**
+     * See {@link #queryBroadcastReceivers(Intent, int)}.
      */
     @NonNull
-    public abstract List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
-            @ResolveInfoFlags int flags);
+    public List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryBroadcastReceivers not implemented in subclass");
+    }
 
     /**
      * Retrieve all receivers that can handle a broadcast of the given intent,
@@ -6410,24 +6828,53 @@
      *         each matching receiver, ordered from best to worst. If there are
      *         no matching receivers, an empty list or null is returned.
      * @hide
+     * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, UserHandle)}
+     * instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @SystemApi
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, UserHandle userHandle) {
+            int flags, UserHandle userHandle) {
+        return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
+    }
+
+    /**
+     * See {@link #queryBroadcastReceiversAsUser(Intent, int, UserHandle)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @NonNull UserHandle userHandle) {
         return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
     }
 
     /**
      * @hide
+     * @deprecated Use {@link #queryBroadcastReceiversAsUser(Intent, ResolveInfoFlags, int)}
+     * instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #queryBroadcastReceiversAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @NonNull
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "queryBroadcastReceiversAsUser not implemented in subclass");
+    }
 
 
     /** @deprecated @hide */
@@ -6435,7 +6882,7 @@
     @Deprecated
     @UnsupportedAppUsage
     public List<ResolveInfo> queryBroadcastReceivers(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId) {
+            int flags, @UserIdInt int userId) {
         final String msg = "Shame on you for calling the hidden API "
                 + "queryBroadcastReceivers(). Shame!";
         if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.O) {
@@ -6455,17 +6902,41 @@
      * @return Returns a ResolveInfo object containing the final service intent
      *         that was determined to be the best action. Returns null if no
      *         matching service was found.
+     * @deprecated Use {@link #resolveService(Intent, ResolveInfoFlags)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public abstract ResolveInfo resolveService(@NonNull Intent intent, int flags);
+
+    /**
+     * See {@link #resolveService(Intent, int)}.
      */
     @Nullable
-    public abstract ResolveInfo resolveService(@NonNull Intent intent, @ResolveInfoFlags int flags);
+    public ResolveInfo resolveService(@NonNull Intent intent, @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "resolveService not implemented in subclass");
+    }
 
     /**
      * @hide
+     * @deprecated Use {@link #resolveServiceAsUser(Intent, ResolveInfoFlags, int)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #resolveServiceAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @Nullable
+    public ResolveInfo resolveServiceAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "resolveServiceAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all services that can match the given intent.
@@ -6477,10 +6948,22 @@
      *         words, the first item is what would be returned by
      *         {@link #resolveService}. If there are no matching services, an
      *         empty list or null is returned.
+     * @deprecated Use {@link #queryIntentServices(Intent, ResolveInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
-            @ResolveInfoFlags int flags);
+            int flags);
+
+    /**
+     * See {@link #queryIntentServices(Intent, int)}.
+     */
+    @NonNull
+    public List<ResolveInfo> queryIntentServices(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryIntentServices not implemented in subclass");
+    }
 
     /**
      * Retrieve all services that can match the given intent for a given user.
@@ -6494,12 +6977,25 @@
      *         {@link #resolveService}. If there are no matching services, an
      *         empty list or null is returned.
      * @hide
+     * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, int)} instead.
      */
+    @Deprecated
     @SuppressWarnings("HiddenAbstractMethod")
     @NonNull
     @UnsupportedAppUsage
     public abstract List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #queryIntentServicesAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @NonNull
+    public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "queryIntentServicesAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all services that can match the given intent for a given user.
@@ -6513,14 +7009,60 @@
      *         {@link #resolveService}. If there are no matching services, an
      *         empty list or null is returned.
      * @hide
+     * @deprecated Use {@link #queryIntentServicesAsUser(Intent, ResolveInfoFlags, UserHandle)}
+     * instead.
+     */
+    @Deprecated
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
+            int flags, @NonNull UserHandle user) {
+        return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * See {@link #queryIntentServicesAsUser(Intent, int, UserHandle)}.
+     * @hide
      */
     @NonNull
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
     public List<ResolveInfo> queryIntentServicesAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+            @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
         return queryIntentServicesAsUser(intent, flags, user.getIdentifier());
     }
+    /**
+     * Retrieve all providers that can match the given intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags to modify the data returned.
+     * @param userId The user id.
+     * @return Returns a List of ResolveInfo objects containing one entry for
+     *         each matching provider, ordered from best to worst. If there are
+     *         no matching services, an empty list or null is returned.
+     * @hide
+     * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags, int)}
+     * instead.
+     */
+    @Deprecated
+    @SuppressWarnings("HiddenAbstractMethod")
+    @NonNull
+    @UnsupportedAppUsage
+    public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
+            @NonNull Intent intent, int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #queryIntentContentProvidersAsUser(Intent, int, int)}.
+     * @hide
+     */
+    @NonNull
+    protected List<ResolveInfo> queryIntentContentProvidersAsUser(
+            @NonNull Intent intent, @NonNull ResolveInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "queryIntentContentProvidersAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve all providers that can match the given intent.
@@ -6528,35 +7070,32 @@
      * @param intent An intent containing all of the desired specification
      *            (action, data, type, category, and/or component).
      * @param flags Additional option flags to modify the data returned.
-     * @param userId The user id.
-     * @return Returns a List of ResolveInfo objects containing one entry for
-     *         each matching provider, ordered from best to worst. If there are
-     *         no matching services, an empty list or null is returned.
-     * @hide
-     */
-    @SuppressWarnings("HiddenAbstractMethod")
-    @NonNull
-    @UnsupportedAppUsage
-    public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
-            @NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId);
-
-    /**
-     * Retrieve all providers that can match the given intent.
-     *
-     * @param intent An intent containing all of the desired specification
-     *            (action, data, type, category, and/or component).
-     * @param flags Additional option flags to modify the data returned.
      * @param user The user being queried.
      * @return Returns a List of ResolveInfo objects containing one entry for
      *         each matching provider, ordered from best to worst. If there are
      *         no matching services, an empty list or null is returned.
      * @hide
+     * @deprecated Use {@link #queryIntentContentProvidersAsUser(Intent, ResolveInfoFlags,
+     * UserHandle)} instead.
+     */
+    @Deprecated
+    @NonNull
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @SystemApi
+    public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
+            int flags, @NonNull UserHandle user) {
+        return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
+    }
+
+    /**
+     * See {@link #queryIntentContentProvidersAsUser(Intent, int, UserHandle)}.
+     * @hide
      */
     @NonNull
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     @SystemApi
     public List<ResolveInfo> queryIntentContentProvidersAsUser(@NonNull Intent intent,
-            @ResolveInfoFlags int flags, @NonNull UserHandle user) {
+            @NonNull ResolveInfoFlags flags, @NonNull UserHandle user) {
         return queryIntentContentProvidersAsUser(intent, flags, user.getIdentifier());
     }
 
@@ -6569,10 +7108,22 @@
      * @return Returns a List of ResolveInfo objects containing one entry for
      *         each matching provider, ordered from best to worst. If there are
      *         no matching services, an empty list or null is returned.
+     * @deprecated Use {@link #queryIntentContentProviders(Intent, ResolveInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
-            @ResolveInfoFlags int flags);
+            int flags);
+
+    /**
+     * See {@link #queryIntentContentProviders(Intent, int)}.
+     */
+    @NonNull
+    public List<ResolveInfo> queryIntentContentProviders(@NonNull Intent intent,
+            @NonNull ResolveInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryIntentContentProviders not implemented in subclass");
+    }
 
     /**
      * Find a single content provider by its authority.
@@ -6587,10 +7138,22 @@
      * @param flags Additional option flags to modify the data returned.
      * @return A {@link ProviderInfo} object containing information about the
      *         provider. If a provider was not found, returns null.
+     * @deprecated Use {@link #resolveContentProvider(String, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @Nullable
     public abstract ProviderInfo resolveContentProvider(@NonNull String authority,
-            @ComponentInfoFlags int flags);
+            int flags);
+
+    /**
+     * See {@link #resolveContentProvider(String, int)}.
+     */
+    @Nullable
+    public ProviderInfo resolveContentProvider(@NonNull String authority,
+            @NonNull ComponentInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "resolveContentProvider not implemented in subclass");
+    }
 
     /**
      * Find a single content provider by its base path name.
@@ -6601,12 +7164,25 @@
      * @return A {@link ProviderInfo} object containing information about the
      *         provider. If a provider was not found, returns null.
      * @hide
+     * @deprecated Use {@link #resolveContentProviderAsUser(String, ComponentInfoFlags, int)}
+     * instead.
      */
     @SuppressWarnings("HiddenAbstractMethod")
     @Nullable
     @UnsupportedAppUsage
     public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
-            @ComponentInfoFlags int flags, @UserIdInt int userId);
+            int flags, @UserIdInt int userId);
+
+    /**
+     * See {@link #resolveContentProviderAsUser(String, int, int)}.
+     * @hide
+     */
+    @Nullable
+    public ProviderInfo resolveContentProviderAsUser(@NonNull String providerName,
+            @NonNull ComponentInfoFlags flags, @UserIdInt int userId) {
+        throw new UnsupportedOperationException(
+                "resolveContentProviderAsUser not implemented in subclass");
+    }
 
     /**
      * Retrieve content provider information.
@@ -6624,10 +7200,22 @@
      *         each provider either matching <var>processName</var> or, if
      *         <var>processName</var> is null, all known content providers.
      *         <em>If there are no matching providers, null is returned.</em>
+     * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags)} instead.
      */
+    @Deprecated
     @NonNull
     public abstract List<ProviderInfo> queryContentProviders(
-            @Nullable String processName, int uid, @ComponentInfoFlags int flags);
+            @Nullable String processName, int uid, int flags);
+
+    /**
+     * See {@link #queryContentProviders(String, int, int)}.
+     */
+    @NonNull
+    public List<ProviderInfo> queryContentProviders(
+            @Nullable String processName, int uid, @NonNull ComponentInfoFlags flags) {
+        throw new UnsupportedOperationException(
+                "queryContentProviders not implemented in subclass");
+    }
 
     /**
      * Same as {@link #queryContentProviders}, except when {@code metaDataKey} is not null,
@@ -6643,10 +7231,24 @@
      * {@link #queryIntentContentProviders} for that.
      *
      * @hide
+     * @deprecated Use {@link #queryContentProviders(String, int, ComponentInfoFlags, String)}
+     * instead.
+     */
+    @Deprecated
+    @NonNull
+    public List<ProviderInfo> queryContentProviders(@Nullable String processName,
+            int uid, int flags, String metaDataKey) {
+        // Provide the default implementation for mocks.
+        return queryContentProviders(processName, uid, flags);
+    }
+
+    /**
+     * See {@link #queryContentProviders(String, int, int, String)}.
+     * @hide
      */
     @NonNull
     public List<ProviderInfo> queryContentProviders(@Nullable String processName,
-            int uid, @ComponentInfoFlags int flags, String metaDataKey) {
+            int uid, @NonNull ComponentInfoFlags flags, @Nullable String metaDataKey) {
         // Provide the default implementation for mocks.
         return queryContentProviders(processName, uid, flags);
     }
@@ -7182,10 +7784,21 @@
      * @param flags Additional option flags to modify the data returned.
      * @return A PackageInfo object containing information about the package
      *         archive. If the package could not be parsed, returns null.
+     * @deprecated Use {@link #getPackageArchiveInfo(String, PackageInfoFlags)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, int flags) {
+        throw new UnsupportedOperationException(
+                "getPackageArchiveInfo() not implemented in subclass");
+    }
+
+    /**
+     * See {@link #getPackageArchiveInfo(String, int)}.
      */
     @Nullable
     public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
-            @PackageInfoFlags int flags) {
+            @NonNull PackageInfoFlags flags) {
         throw new UnsupportedOperationException(
                 "getPackageArchiveInfo() not implemented in subclass");
     }
@@ -7761,7 +8374,7 @@
      */
     @NonNull
     @Deprecated
-    public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
+    public abstract List<PackageInfo> getPreferredPackages(int flags);
 
     /**
      * Add a new preferred activity mapping to the system.  This will be used
@@ -9437,10 +10050,11 @@
 
     private static final class ApplicationInfoQuery {
         final String packageName;
-        final int flags;
+        final long flags;
         final int userId;
 
-        ApplicationInfoQuery(@Nullable String packageName, int flags, int userId) {
+        ApplicationInfoQuery(@Nullable String packageName, @ApplicationInfoFlagsBits long flags,
+                int userId) {
             this.packageName = packageName;
             this.flags = flags;
             this.userId = userId;
@@ -9479,7 +10093,7 @@
     }
 
     private static ApplicationInfo getApplicationInfoAsUserUncached(
-            String packageName, int flags, int userId) {
+            String packageName, @ApplicationInfoFlagsBits long flags, int userId) {
         try {
             return ActivityThread.getPackageManager()
                     .getApplicationInfo(packageName, flags, userId);
@@ -9509,7 +10123,7 @@
 
     /** @hide */
     public static ApplicationInfo getApplicationInfoAsUserCached(
-            String packageName, int flags, int userId) {
+            String packageName, @ApplicationInfoFlagsBits long flags, int userId) {
         return sApplicationInfoCache.query(
                 new ApplicationInfoQuery(packageName, flags, userId));
     }
@@ -9540,10 +10154,10 @@
 
     private static final class PackageInfoQuery {
         final String packageName;
-        final int flags;
+        final long flags;
         final int userId;
 
-        PackageInfoQuery(@Nullable String packageName, int flags, int userId) {
+        PackageInfoQuery(@Nullable String packageName, @PackageInfoFlagsBits long flags, int userId) {
             this.packageName = packageName;
             this.flags = flags;
             this.userId = userId;
@@ -9582,7 +10196,7 @@
     }
 
     private static PackageInfo getPackageInfoAsUserUncached(
-            String packageName, int flags, int userId) {
+            String packageName, @PackageInfoFlagsBits long flags, int userId) {
         try {
             return ActivityThread.getPackageManager().getPackageInfo(packageName, flags, userId);
         } catch (RemoteException e) {
@@ -9611,7 +10225,7 @@
 
     /** @hide */
     public static PackageInfo getPackageInfoAsUserCached(
-            String packageName, int flags, int userId) {
+            String packageName, @PackageInfoFlagsBits long flags, int userId) {
         return sPackageInfoCache.query(new PackageInfoQuery(packageName, flags, userId));
     }
 
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b11b38a..28290d7 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -76,8 +76,9 @@
 
     @Nullable
     public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
-            Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+            int userId) {
         return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
                 state, userId, null);
     }
@@ -90,9 +91,9 @@
 
     @Nullable
     private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
-            Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
-            @Nullable ApexInfo apexInfo) {
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+            int userId, @Nullable ApexInfo apexInfo) {
         ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
         if (applicationInfo == null) {
             return null;
@@ -190,9 +191,9 @@
 
     @Nullable
     public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime, long lastUpdateTime,
-            Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
-            @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
+            long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
+            int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
         if (!checkUseInstalled(pkg, state, flags)) {
             return null;
         }
@@ -210,7 +211,7 @@
      */
     @NonNull
     public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
             long lastUpdateTime, Set<String> grantedPermissions, FrameworkPackageUserState state,
             int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
         PackageInfo pi = new PackageInfo();
@@ -365,7 +366,7 @@
 
     @Nullable
     public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
-            @PackageManager.ApplicationInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ApplicationInfoFlagsBits long flags, FrameworkPackageUserState state,
             int userId) {
         if (pkg == null) {
             return null;
@@ -393,7 +394,7 @@
      */
     @NonNull
     public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
-            @PackageManager.ApplicationInfoFlags long flags,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
             @NonNull FrameworkPackageUserState state, int userId, boolean assignUserFields) {
         // Make shallow copy so we can store the metadata/libraries safely
         ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
@@ -453,7 +454,7 @@
 
     @Nullable
     public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
-            @PackageManager.ApplicationInfoFlags long flags,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
             @NonNull FrameworkPackageUserState state, int userId) {
         if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
             return null;
@@ -470,8 +471,8 @@
 
     @Nullable
     public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
-            @PackageManager.ComponentInfoFlags long flags, @NonNull FrameworkPackageUserState state,
-            int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull FrameworkPackageUserState state, int userId) {
         if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
             return null;
         }
@@ -485,7 +486,7 @@
 
     @Nullable
     public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId) {
         if (a == null) return null;
         if (!checkUseInstalled(pkg, state, flags)) {
@@ -510,7 +511,7 @@
      */
     @NonNull
     public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
-            @PackageManager.ComponentInfoFlags long flags,
+            @PackageManager.ComponentInfoFlagsBits long flags,
             @NonNull ApplicationInfo applicationInfo) {
         // Make shallow copies so we can store the metadata safely
         ActivityInfo ai = new ActivityInfo();
@@ -551,14 +552,14 @@
 
     @Nullable
     public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             int userId) {
         return generateActivityInfo(pkg, a, flags, state, null, userId);
     }
 
     @Nullable
     public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId) {
         if (s == null) return null;
         if (!checkUseInstalled(pkg, state, flags)) {
@@ -583,7 +584,7 @@
      */
     @NonNull
     public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
-            @PackageManager.ComponentInfoFlags long flags,
+            @PackageManager.ComponentInfoFlagsBits long flags,
             @NonNull ApplicationInfo applicationInfo) {
         // Make shallow copies so we can store the metadata safely
         ServiceInfo si = new ServiceInfo();
@@ -602,14 +603,14 @@
 
     @Nullable
     public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             int userId) {
         return generateServiceInfo(pkg, s, flags, state, null, userId);
     }
 
     @Nullable
     public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId) {
         if (p == null) return null;
         if (!checkUseInstalled(pkg, state, flags)) {
@@ -634,7 +635,7 @@
      */
     @NonNull
     public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
-            @PackageManager.ComponentInfoFlags long flags,
+            @PackageManager.ComponentInfoFlagsBits long flags,
             @NonNull ApplicationInfo applicationInfo) {
         // Make shallow copies so we can store the metadata safely
         ProviderInfo pi = new ProviderInfo();
@@ -664,7 +665,7 @@
 
     @Nullable
     public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlags long flags, FrameworkPackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, FrameworkPackageUserState state,
             int userId) {
         return generateProviderInfo(pkg, p, flags, state, null, userId);
     }
@@ -675,7 +676,7 @@
      */
     @Nullable
     public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
-            ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
+            ParsingPackageRead pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
             boolean assignUserFields) {
         if (i == null) return null;
 
@@ -706,7 +707,7 @@
 
     @Nullable
     public static PermissionInfo generatePermissionInfo(ParsedPermission p,
-            @PackageManager.ComponentInfoFlags long flags) {
+            @PackageManager.ComponentInfoFlagsBits long flags) {
         if (p == null) return null;
 
         PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
@@ -729,7 +730,7 @@
 
     @Nullable
     public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
-            @PackageManager.ComponentInfoFlags long flags) {
+            @PackageManager.ComponentInfoFlagsBits long flags) {
         if (pg == null) return null;
 
         PermissionGroupInfo pgi = new PermissionGroupInfo(
@@ -887,7 +888,7 @@
     }
 
     private static boolean checkUseInstalled(ParsingPackageRead pkg,
-            FrameworkPackageUserState state, @PackageManager.PackageInfoFlags long flags) {
+            FrameworkPackageUserState state, @PackageManager.PackageInfoFlagsBits long flags) {
         // If available for the target user
         return PackageUserStateUtils.isAvailable(state, flags);
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 63332e7..2fa5df7 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedApexSystemService;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -56,6 +57,8 @@
 
     ParsingPackage addAdoptPermission(String adoptPermission);
 
+    ParsingPackage addApexSystemService(ParsedApexSystemService parsedApexSystemService);
+
     ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
 
     ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 19a8ce9..f03ab6a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -35,6 +35,8 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityImpl;
+import android.content.pm.parsing.component.ParsedApexSystemService;
+import android.content.pm.parsing.component.ParsedApexSystemServiceImpl;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedAttributionImpl;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -268,6 +270,9 @@
     protected List<ParsedActivity> activities = emptyList();
 
     @NonNull
+    protected List<ParsedApexSystemService> apexSystemServices = emptyList();
+
+    @NonNull
     protected List<ParsedActivity> receivers = emptyList();
 
     @NonNull
@@ -764,6 +769,14 @@
     }
 
     @Override
+    public final ParsingPackageImpl addApexSystemService(
+            ParsedApexSystemService parsedApexSystemService) {
+        this.apexSystemServices = CollectionUtils.add(
+                this.apexSystemServices, parsedApexSystemService);
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
         this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
         addMimeGroupsFromComponent(parsedReceiver);
@@ -832,7 +845,6 @@
         return this;
     }
 
-
     @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
         this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
                 libraryName);
@@ -1198,6 +1210,7 @@
         ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
         sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
         dest.writeTypedList(this.activities);
+        dest.writeTypedList(this.apexSystemServices);
         dest.writeTypedList(this.receivers);
         dest.writeTypedList(this.services);
         dest.writeTypedList(this.providers);
@@ -1339,6 +1352,8 @@
         this.protectedBroadcasts = sForInternedStringList.unparcel(in);
 
         this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+        this.apexSystemServices = ParsingUtils.createTypedInterfaceList(in,
+                ParsedApexSystemServiceImpl.CREATOR);
         this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
         this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR);
         this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR);
@@ -1706,6 +1721,12 @@
 
     @NonNull
     @Override
+    public List<ParsedApexSystemService> getApexSystemServices() {
+        return apexSystemServices;
+    }
+
+    @NonNull
+    @Override
     public List<ParsedActivity> getReceivers() {
         return receivers;
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 49b3b08..c8113ef 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageParser;
 import android.content.pm.SigningDetails;
+import android.content.pm.parsing.component.ParsedApexSystemService;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
@@ -55,6 +56,12 @@
     @NonNull
     List<String> getAdoptPermissions();
 
+    /**
+     * @see R.styleable#AndroidManifestApexSystemService
+     */
+    @NonNull
+    List<ParsedApexSystemService> getApexSystemServices();
+
     @NonNull
     List<ParsedAttribution> getAttributions();
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3e537c8..fb24cb2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -53,6 +53,8 @@
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityUtils;
+import android.content.pm.parsing.component.ParsedApexSystemService;
+import android.content.pm.parsing.component.ParsedApexSystemServiceUtils;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedAttributionUtils;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -2205,6 +2207,18 @@
 
                     result = activityResult;
                     break;
+                case "apex-system-service":
+                    ParseResult<ParsedApexSystemService> systemServiceResult =
+                            ParsedApexSystemServiceUtils.parseApexSystemService(res,
+                                    parser, input);
+                    if (systemServiceResult.isSuccess()) {
+                        ParsedApexSystemService systemService =
+                                systemServiceResult.getResult();
+                        pkg.addApexSystemService(systemService);
+                    }
+
+                    result = systemServiceResult;
+                    break;
                 default:
                     result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                     break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
new file mode 100644
index 0000000..fe821e0
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemService.java
@@ -0,0 +1,38 @@
+/*
+ * 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+/** @hide */
+public interface ParsedApexSystemService extends Parcelable {
+
+    @NonNull
+    String getName();
+
+    @Nullable
+    String getJarPath();
+
+    @Nullable
+    String getMinSdkVersion();
+
+    @Nullable
+    String getMaxSdkVersion();
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
new file mode 100644
index 0000000..54196fd
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.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 android.content.pm.parsing.component;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/** @hide **/
+@DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedApexSystemServiceImpl implements ParsedApexSystemService {
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @NonNull
+    private String name;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String jarPath;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String minSdkVersion;
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @Nullable
+    private String maxSdkVersion;
+
+    public ParsedApexSystemServiceImpl() {
+    }
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedApexSystemServiceImpl(
+            @NonNull String name,
+            @Nullable String jarPath,
+            @Nullable String minSdkVersion,
+            @Nullable String maxSdkVersion) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.jarPath = jarPath;
+        this.minSdkVersion = minSdkVersion;
+        this.maxSdkVersion = maxSdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getJarPath() {
+        return jarPath;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getMaxSdkVersion() {
+        return maxSdkVersion;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setJarPath(@NonNull String value) {
+        jarPath = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setMinSdkVersion(@NonNull String value) {
+        minSdkVersion = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setMaxSdkVersion(@NonNull String value) {
+        maxSdkVersion = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForName =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForName == null) {
+            sParcellingForName = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForJarPath =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForJarPath == null) {
+            sParcellingForJarPath = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForMinSdkVersion =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForMinSdkVersion == null) {
+            sParcellingForMinSdkVersion = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForMaxSdkVersion =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForMaxSdkVersion == null) {
+            sParcellingForMaxSdkVersion = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (jarPath != null) flg |= 0x2;
+        if (minSdkVersion != null) flg |= 0x4;
+        if (maxSdkVersion != null) flg |= 0x8;
+        dest.writeByte(flg);
+        sParcellingForName.parcel(name, dest, flags);
+        sParcellingForJarPath.parcel(jarPath, dest, flags);
+        sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
+        sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedApexSystemServiceImpl(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String _name = sParcellingForName.unparcel(in);
+        String _jarPath = sParcellingForJarPath.unparcel(in);
+        String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
+        String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.jarPath = _jarPath;
+        this.minSdkVersion = _minSdkVersion;
+        this.maxSdkVersion = _maxSdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull android.os.Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
+            = new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
+        @Override
+        public ParsedApexSystemServiceImpl[] newArray(int size) {
+            return new ParsedApexSystemServiceImpl[size];
+        }
+
+        @Override
+        public ParsedApexSystemServiceImpl createFromParcel(@NonNull android.os.Parcel in) {
+            return new ParsedApexSystemServiceImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1638903241144L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
new file mode 100644
index 0000000..26abf48
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceUtils.java
@@ -0,0 +1,69 @@
+/*
+ * 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.parsing.component;
+
+import android.R;
+import android.annotation.NonNull;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedApexSystemServiceUtils {
+
+    @NonNull
+    public static ParseResult<ParsedApexSystemService> parseApexSystemService(
+            Resources res, XmlResourceParser parser, ParseInput input)
+            throws XmlPullParserException, IOException {
+        final ParsedApexSystemServiceImpl systemService =
+                new ParsedApexSystemServiceImpl();
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestApexSystemService);
+        try {
+            String className = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_name);
+            if (TextUtils.isEmpty(className)) {
+                return input.error("<apex-system-service> does not have name attribute");
+            }
+
+            String jarPath = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_path);
+            String minSdkVersion = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_minSdkVersion);
+            String maxSdkVersion = sa.getString(
+                    R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
+
+            systemService.setName(className)
+                    .setMinSdkVersion(minSdkVersion)
+                    .setMaxSdkVersion(maxSdkVersion);
+            if (!TextUtils.isEmpty(jarPath)) {
+                systemService.setJarPath(jarPath);
+            }
+
+            return input.success(systemService);
+        } finally {
+            sa.recycle();
+        }
+    }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 632d8e5..b5d4f09 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -80,6 +80,7 @@
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
@@ -1942,6 +1943,16 @@
             final Theme other = (Theme) o;
             return getKey().equals(other.getKey());
         }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append('{');
+            sb.append("id=0x").append(Integer.toHexString(getAppliedStyleResId())).append(", ");
+            sb.append("themes=").append(Arrays.deepToString(getTheme()));
+            sb.append('}');
+            return sb.toString();
+        }
     }
 
     static class ThemeKey implements Cloneable {
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index d6f55d6..9eb9cd5 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -527,11 +527,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to color: type=0x" + Integer.toHexString(type));
+                + " to color: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -561,7 +562,8 @@
         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
-                        "Failed to resolve attribute at index " + index + ": " + value);
+                        "Failed to resolve attribute at index " + index + ": " + value
+                                + ", theme=" + mTheme);
             }
             return mResources.loadComplexColor(value, value.resourceId, mTheme);
         }
@@ -596,7 +598,8 @@
         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
-                        "Failed to resolve attribute at index " + index + ": " + value);
+                        "Failed to resolve attribute at index " + index + ": " + value
+                                + ", theme=" + mTheme);
             }
             return mResources.loadColorStateList(value, value.resourceId, mTheme);
         }
@@ -637,11 +640,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to integer: type=0x" + Integer.toHexString(type));
+                + " to integer: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -684,11 +688,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to dimension: type=0x" + Integer.toHexString(type));
+                + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -732,11 +737,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to dimension: type=0x" + Integer.toHexString(type));
+                + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -781,11 +787,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to dimension: type=0x" + Integer.toHexString(type));
+                + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -825,11 +832,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException(getPositionDescription()
-                + ": You must supply a " + name + " attribute.");
+                + ": You must supply a " + name + " attribute." + ", theme=" + mTheme);
     }
 
     /**
@@ -900,11 +908,12 @@
             final TypedValue value = mValue;
             getValueAt(index, value);
             throw new UnsupportedOperationException(
-                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
+                    "Failed to resolve attribute at index " + attrIndex + ": " + value
+                            + ", theme=" + mTheme);
         }
 
         throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
-                + " to fraction: type=0x" + Integer.toHexString(type));
+                + " to fraction: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
     }
 
     /**
@@ -996,7 +1005,8 @@
         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
-                        "Failed to resolve attribute at index " + index + ": " + value);
+                        "Failed to resolve attribute at index " + index + ": " + value
+                                + ", theme=" + mTheme);
             }
 
             if (density > 0) {
@@ -1032,7 +1042,8 @@
         if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
-                        "Failed to resolve attribute at index " + index + ": " + value);
+                        "Failed to resolve attribute at index " + index + ": " + value
+                                + ", theme=" + mTheme);
             }
             return mResources.getFont(value, value.resourceId);
         }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3c8b6e9..48a9121 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2669,7 +2669,8 @@
      * </tbody>
      * </table>
      * <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be
-     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S,
+     * media performance class 12 or higher by setting
+     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
      * the primary camera devices (first rear/front camera in the camera ID list) will not
      * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream
      * smaller than 1080p, the camera device will round up the JPEG image size to at least
@@ -2742,9 +2743,11 @@
      * </tbody>
      * </table>
      * <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare
-     * to be media performance class S, or if the camera device isn't a primary rear/front
-     * camera, the minimum required output stream configurations are the same as for applications
-     * targeting SDK version older than 31.</p>
+     * to be media performance class 12 or better by setting
+     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
+     * or if the camera device isn't a primary rear/front camera, the minimum required output
+     * stream configurations are the same as for applications targeting SDK version older than
+     * 31.</p>
      * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
      * mandatory stream configurations on a per-capability basis.</p>
      * <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.aidl b/core/java/android/hardware/input/VirtualKeyEvent.aidl
new file mode 100644
index 0000000..5b3ee0c
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualKeyEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualKeyEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
new file mode 100644
index 0000000..d875156
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualKeyEvent.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 android.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.KeyEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a keyboard interaction originating from a remote device.
+ *
+ * When the user presses a key, an {@code ACTION_DOWN} event should be reported. When the user
+ * releases the key, an {@code ACTION_UP} event should be reported.
+ *
+ * See {@link android.view.KeyEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualKeyEvent implements Parcelable {
+
+    /** @hide */
+    public static final int ACTION_UNKNOWN = -1;
+    /** Action indicating the given key has been pressed. */
+    public static final int ACTION_DOWN = KeyEvent.ACTION_DOWN;
+    /** Action indicating the previously pressed key has been lifted. */
+    public static final int ACTION_UP = KeyEvent.ACTION_UP;
+
+    /** @hide */
+    @IntDef(prefix = { "ACTION_" }, value = {
+            ACTION_UNKNOWN,
+            ACTION_DOWN,
+            ACTION_UP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Action {
+    }
+
+    private final @Action int mAction;
+    private final int mKeyCode;
+
+    private VirtualKeyEvent(@Action int action, int keyCode) {
+        mAction = action;
+        mKeyCode = keyCode;
+    }
+
+    private VirtualKeyEvent(@NonNull Parcel parcel) {
+        mAction = parcel.readInt();
+        mKeyCode = parcel.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeInt(mAction);
+        parcel.writeInt(mKeyCode);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the key code associated with this event.
+     */
+    public int getKeyCode() {
+        return mKeyCode;
+    }
+
+    /**
+     * Returns the action associated with this event.
+     */
+    public @Action int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Builder for {@link VirtualKeyEvent}.
+     */
+    public static final class Builder {
+
+        private @Action int mAction = ACTION_UNKNOWN;
+        private int mKeyCode = -1;
+
+        /**
+         * Creates a {@link VirtualKeyEvent} object with the current builder configuration.
+         */
+        public @NonNull VirtualKeyEvent build() {
+            if (mAction == ACTION_UNKNOWN || mKeyCode == -1) {
+                throw new IllegalArgumentException(
+                        "Cannot build virtual key event with unset fields");
+            }
+            return new VirtualKeyEvent(mAction, mKeyCode);
+        }
+
+        /**
+         * Sets the Android key code of the event. The set of allowed characters include digits 0-9,
+         * characters A-Z, and standard punctuation, as well as numpad keys, function keys F1-F12,
+         * and meta keys (caps lock, shift, etc.).
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setKeyCode(int keyCode) {
+            mKeyCode = keyCode;
+            return this;
+        }
+
+        /**
+         * Sets the action of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setAction(@Action int action) {
+            if (action != ACTION_DOWN && action != ACTION_UP) {
+                throw new IllegalArgumentException("Unsupported action type");
+            }
+            mAction = action;
+            return this;
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<VirtualKeyEvent> CREATOR =
+            new Parcelable.Creator<VirtualKeyEvent>() {
+        public VirtualKeyEvent createFromParcel(Parcel source) {
+            return new VirtualKeyEvent(source);
+        }
+
+        public VirtualKeyEvent[] newArray(int size) {
+            return new VirtualKeyEvent[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
new file mode 100644
index 0000000..ee9b659
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.Closeable;
+
+/**
+ * A virtual keyboard representing a key input mechanism on a remote device, such as a built-in
+ * keyboard on a laptop, a software keyboard on a tablet, or a keypad on a TV remote control.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualKeyboard implements Closeable {
+
+    private final IVirtualDevice mVirtualDevice;
+    private final IBinder mToken;
+
+    /** @hide */
+    public VirtualKeyboard(IVirtualDevice virtualDevice, IBinder token) {
+        mVirtualDevice = virtualDevice;
+        mToken = token;
+    }
+
+    @Override
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void close() {
+        try {
+            mVirtualDevice.unregisterInputDevice(mToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sends a key event to the system.
+     *
+     * @param event the event to send
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
+        try {
+            mVirtualDevice.sendKeyEvent(mToken, event);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
new file mode 100644
index 0000000..6599dd2
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -0,0 +1,102 @@
+/*
+ * 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.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.MotionEvent;
+
+import java.io.Closeable;
+
+/**
+ * A virtual mouse representing a relative input mechanism on a remote device, such as a mouse or
+ * trackpad.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualMouse implements Closeable {
+
+    private final IVirtualDevice mVirtualDevice;
+    private final IBinder mToken;
+
+    /** @hide */
+    public VirtualMouse(IVirtualDevice virtualDevice, IBinder token) {
+        mVirtualDevice = virtualDevice;
+        mToken = token;
+    }
+
+    @Override
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void close() {
+        try {
+            mVirtualDevice.unregisterInputDevice(mToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Send a mouse button event to the system.
+     *
+     * @param event the event
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
+        try {
+            mVirtualDevice.sendButtonEvent(mToken, event);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sends a scrolling event to the system. See {@link MotionEvent#AXIS_VSCROLL} and
+     * {@link MotionEvent#AXIS_SCROLL}.
+     *
+     * @param event the event
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
+        try {
+            mVirtualDevice.sendScrollEvent(mToken, event);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sends a relative movement event to the system.
+     *
+     * @param event the event
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
+        try {
+            mVirtualDevice.sendRelativeEvent(mToken, event);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/hardware/input/VirtualMouseButtonEvent.aidl b/core/java/android/hardware/input/VirtualMouseButtonEvent.aidl
new file mode 100644
index 0000000..ebcf5aa
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualMouseButtonEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseButtonEvent.java b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
new file mode 100644
index 0000000..2e094cf
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
@@ -0,0 +1,180 @@
+/*
+ * 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.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a mouse button click interaction originating from a remote device.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseButtonEvent implements Parcelable {
+
+    /** @hide */
+    public static final int ACTION_UNKNOWN = -1;
+    /** Action indicating the mouse button has been pressed. */
+    public static final int ACTION_BUTTON_PRESS = MotionEvent.ACTION_BUTTON_PRESS;
+    /** Action indicating the mouse button has been released. */
+    public static final int ACTION_BUTTON_RELEASE = MotionEvent.ACTION_BUTTON_RELEASE;
+    /** @hide */
+    @IntDef(prefix = {"ACTION_"}, value = {
+            ACTION_UNKNOWN,
+            ACTION_BUTTON_PRESS,
+            ACTION_BUTTON_RELEASE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Action {}
+
+    /** @hide */
+    public static final int BUTTON_UNKNOWN = -1;
+    /** Action indicating the mouse button involved in this event is in the left position. */
+    public static final int BUTTON_PRIMARY = MotionEvent.BUTTON_PRIMARY;
+    /** Action indicating the mouse button involved in this event is in the middle position. */
+    public static final int BUTTON_TERTIARY = MotionEvent.BUTTON_TERTIARY;
+    /** Action indicating the mouse button involved in this event is in the right position. */
+    public static final int BUTTON_SECONDARY = MotionEvent.BUTTON_SECONDARY;
+    /**
+     * Action indicating the mouse button involved in this event is intended to go back to the
+     * previous.
+     */
+    public static final int BUTTON_BACK = MotionEvent.BUTTON_BACK;
+    /**
+     * Action indicating the mouse button involved in this event is intended to move forward to the
+     * next.
+     */
+    public static final int BUTTON_FORWARD = MotionEvent.BUTTON_FORWARD;
+    /** @hide */
+    @IntDef(prefix = {"BUTTON_"}, value = {
+            BUTTON_UNKNOWN,
+            BUTTON_PRIMARY,
+            BUTTON_TERTIARY,
+            BUTTON_SECONDARY,
+            BUTTON_BACK,
+            BUTTON_FORWARD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Button {}
+
+    private final @Action int mAction;
+    private final @Button int mButtonCode;
+
+    private VirtualMouseButtonEvent(@Action int action, @Button int buttonCode) {
+        mAction = action;
+        mButtonCode = buttonCode;
+    }
+
+    private VirtualMouseButtonEvent(@NonNull Parcel parcel) {
+        mAction = parcel.readInt();
+        mButtonCode = parcel.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeInt(mAction);
+        parcel.writeInt(mButtonCode);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the button code associated with this event.
+     */
+    public @Button int getButtonCode() {
+        return mButtonCode;
+    }
+
+    /**
+     * Returns the action associated with this event.
+     */
+    public @Action int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Builder for {@link VirtualMouseButtonEvent}.
+     */
+    public static final class Builder {
+
+        private @Action int mAction = ACTION_UNKNOWN;
+        private @Button int mButtonCode = -1;
+
+        /**
+         * Creates a {@link VirtualMouseButtonEvent} object with the current builder configuration.
+         */
+        public @NonNull VirtualMouseButtonEvent build() {
+            if (mAction == ACTION_UNKNOWN || mButtonCode == -1) {
+                throw new IllegalArgumentException(
+                        "Cannot build virtual mouse button event with unset fields");
+            }
+            return new VirtualMouseButtonEvent(mAction, mButtonCode);
+        }
+
+        /**
+         * Sets the button code of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setButtonCode(int buttonCode) {
+            if (buttonCode != BUTTON_PRIMARY
+                    && buttonCode != BUTTON_TERTIARY
+                    && buttonCode != BUTTON_SECONDARY
+                    && buttonCode != BUTTON_BACK
+                    && buttonCode != BUTTON_FORWARD) {
+                throw new IllegalArgumentException("Unsupported mouse button code");
+            }
+            mButtonCode = buttonCode;
+            return this;
+        }
+
+        /**
+         * Sets the action of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setAction(@Action int action) {
+            if (action != ACTION_BUTTON_PRESS && action != ACTION_BUTTON_RELEASE) {
+                throw new IllegalArgumentException("Unsupported mouse button action type");
+            }
+            mAction = action;
+            return this;
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<VirtualMouseButtonEvent> CREATOR =
+            new Parcelable.Creator<VirtualMouseButtonEvent>() {
+                public VirtualMouseButtonEvent createFromParcel(Parcel source) {
+                    return new VirtualMouseButtonEvent(source);
+                }
+
+                public VirtualMouseButtonEvent[] newArray(int size) {
+                    return new VirtualMouseButtonEvent[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl b/core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl
new file mode 100644
index 0000000..1095858
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualMouseRelativeEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
new file mode 100644
index 0000000..65ed1f2
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
@@ -0,0 +1,119 @@
+/*
+ * 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.input;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event describing a mouse movement interaction originating from a remote device.
+ *
+ * See {@link android.view.MotionEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseRelativeEvent implements Parcelable {
+
+    private final float mRelativeX;
+    private final float mRelativeY;
+
+    private VirtualMouseRelativeEvent(float relativeX, float relativeY) {
+        mRelativeX = relativeX;
+        mRelativeY = relativeY;
+    }
+
+    private VirtualMouseRelativeEvent(@NonNull Parcel parcel) {
+        mRelativeX = parcel.readFloat();
+        mRelativeY = parcel.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeFloat(mRelativeX);
+        parcel.writeFloat(mRelativeY);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the relative x-axis movement, in pixels.
+     */
+    public float getRelativeX() {
+        return mRelativeX;
+    }
+
+    /**
+     * Returns the relative x-axis movement, in pixels.
+     */
+    public float getRelativeY() {
+        return mRelativeY;
+    }
+
+    /**
+     * Builder for {@link VirtualMouseRelativeEvent}.
+     */
+    public static final class Builder {
+
+        private float mRelativeX;
+        private float mRelativeY;
+
+        /**
+         * Creates a {@link VirtualMouseRelativeEvent} object with the current builder
+         * configuration.
+         */
+        public @NonNull VirtualMouseRelativeEvent build() {
+            return new VirtualMouseRelativeEvent(mRelativeX, mRelativeY);
+        }
+
+        /**
+         * Sets the relative x-axis movement, in pixels.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setRelativeX(float relativeX) {
+            mRelativeX = relativeX;
+            return this;
+        }
+
+        /**
+         * Sets the relative y-axis movement, in pixels.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setRelativeY(float relativeY) {
+            mRelativeY = relativeY;
+            return this;
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<VirtualMouseRelativeEvent> CREATOR =
+            new Parcelable.Creator<VirtualMouseRelativeEvent>() {
+                public VirtualMouseRelativeEvent createFromParcel(Parcel source) {
+                    return new VirtualMouseRelativeEvent(source);
+                }
+
+                public VirtualMouseRelativeEvent[] newArray(int size) {
+                    return new VirtualMouseRelativeEvent[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/input/VirtualMouseScrollEvent.aidl b/core/java/android/hardware/input/VirtualMouseScrollEvent.aidl
new file mode 100644
index 0000000..13177ef
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualMouseScrollEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualMouseScrollEvent.java b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
new file mode 100644
index 0000000..1723259
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
@@ -0,0 +1,129 @@
+/*
+ * 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.input;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * An event describing a mouse scroll interaction originating from a remote device.
+ *
+ * See {@link android.view.MotionEvent}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualMouseScrollEvent implements Parcelable {
+
+    private final float mXAxisMovement;
+    private final float mYAxisMovement;
+
+    private VirtualMouseScrollEvent(float xAxisMovement, float yAxisMovement) {
+        mXAxisMovement = xAxisMovement;
+        mYAxisMovement = yAxisMovement;
+    }
+
+    private VirtualMouseScrollEvent(@NonNull Parcel parcel) {
+        mXAxisMovement = parcel.readFloat();
+        mYAxisMovement = parcel.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeFloat(mXAxisMovement);
+        parcel.writeFloat(mYAxisMovement);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the x-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+     * indicate scrolling upward; negative values, downward.
+     */
+    public float getXAxisMovement() {
+        return mXAxisMovement;
+    }
+
+    /**
+     * Returns the y-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+     * indicate scrolling towards the right; negative values, to the left.
+     */
+    public float getYAxisMovement() {
+        return mYAxisMovement;
+    }
+
+    /**
+     * Builder for {@link VirtualMouseScrollEvent}.
+     */
+    public static final class Builder {
+
+        private float mXAxisMovement;
+        private float mYAxisMovement;
+
+        /**
+         * Creates a {@link VirtualMouseScrollEvent} object with the current builder configuration.
+         */
+        public @NonNull VirtualMouseScrollEvent build() {
+            return new VirtualMouseScrollEvent(mXAxisMovement, mYAxisMovement);
+        }
+
+        /**
+         * Sets the x-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+         * indicate scrolling upward; negative values, downward.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setXAxisMovement(
+                @FloatRange(from = -1.0f, to = 1.0f) float xAxisMovement) {
+            Preconditions.checkArgumentInRange(xAxisMovement, -1f, 1f, "xAxisMovement");
+            mXAxisMovement = xAxisMovement;
+            return this;
+        }
+
+        /**
+         * Sets the y-axis scroll movement, normalized from -1.0 to 1.0, inclusive. Positive values
+         * indicate scrolling towards the right; negative values, to the left.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setYAxisMovement(
+                @FloatRange(from = -1.0f, to = 1.0f) float yAxisMovement) {
+            Preconditions.checkArgumentInRange(yAxisMovement, -1f, 1f, "yAxisMovement");
+            mYAxisMovement = yAxisMovement;
+            return this;
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<VirtualMouseScrollEvent> CREATOR =
+            new Parcelable.Creator<VirtualMouseScrollEvent>() {
+                public VirtualMouseScrollEvent createFromParcel(Parcel source) {
+                    return new VirtualMouseScrollEvent(source);
+                }
+
+                public VirtualMouseScrollEvent[] newArray(int size) {
+                    return new VirtualMouseScrollEvent[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.aidl b/core/java/android/hardware/input/VirtualTouchEvent.aidl
new file mode 100644
index 0000000..03c82e3
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualTouchEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualTouchEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.java b/core/java/android/hardware/input/VirtualTouchEvent.java
new file mode 100644
index 0000000..c7450d8
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualTouchEvent.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 android.hardware.input;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event describing a touchscreen interaction originating from a remote device.
+ *
+ * The pointer id, tool type, action, and location are required; pressure and main axis size are
+ * optional.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VirtualTouchEvent implements Parcelable {
+
+    /** @hide */
+    public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN;
+    /** Tool type indicating that the user's finger is the origin of the event. */
+    public static final int TOOL_TYPE_FINGER = MotionEvent.TOOL_TYPE_FINGER;
+    /**
+     * Tool type indicating that a user's palm (or other input mechanism to be rejected) is the
+     * origin of the event.
+     */
+    public static final int TOOL_TYPE_PALM = MotionEvent.TOOL_TYPE_PALM;
+    /** @hide */
+    @IntDef(prefix = { "TOOL_TYPE_" }, value = {
+            TOOL_TYPE_UNKNOWN,
+            TOOL_TYPE_FINGER,
+            TOOL_TYPE_PALM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ToolType {}
+
+    /** @hide */
+    public static final int ACTION_UNKNOWN = -1;
+    /** Action indicating the tool has been pressed down to the touchscreen. */
+    public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN;
+    /** Action indicating the tool has been lifted from the touchscreen. */
+    public static final int ACTION_UP = MotionEvent.ACTION_UP;
+    /** Action indicating the tool has been moved along the face of the touchscreen. */
+    public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE;
+    /** Action indicating the tool cancelled the current movement. */
+    public static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL;
+    /** @hide */
+    @IntDef(prefix = { "ACTION_" }, value = {
+            ACTION_UNKNOWN,
+            ACTION_DOWN,
+            ACTION_UP,
+            ACTION_MOVE,
+            ACTION_CANCEL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Action {}
+
+    private final int mPointerId;
+    private final @ToolType int mToolType;
+    private final @Action int mAction;
+    private final float mX;
+    private final float mY;
+    private final float mPressure;
+    private final float mMajorAxisSize;
+
+    private VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action,
+            float x, float y, float pressure, float majorAxisSize) {
+        mPointerId = pointerId;
+        mToolType = toolType;
+        mAction = action;
+        mX = x;
+        mY = y;
+        mPressure = pressure;
+        mMajorAxisSize = majorAxisSize;
+    }
+
+    private VirtualTouchEvent(@NonNull Parcel parcel) {
+        mPointerId = parcel.readInt();
+        mToolType = parcel.readInt();
+        mAction = parcel.readInt();
+        mX = parcel.readFloat();
+        mY = parcel.readFloat();
+        mPressure = parcel.readFloat();
+        mMajorAxisSize = parcel.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPointerId);
+        dest.writeInt(mToolType);
+        dest.writeInt(mAction);
+        dest.writeFloat(mX);
+        dest.writeFloat(mY);
+        dest.writeFloat(mPressure);
+        dest.writeFloat(mMajorAxisSize);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the pointer id associated with this event.
+     */
+    public int getPointerId() {
+        return mPointerId;
+    }
+
+    /**
+     * Returns the tool type associated with this event.
+     */
+    public @ToolType int getToolType() {
+        return mToolType;
+    }
+
+    /**
+     * Returns the action associated with this event.
+     */
+    public @Action int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Returns the x-axis location associated with this event.
+     */
+    public float getX() {
+        return mX;
+    }
+
+    /**
+     * Returns the y-axis location associated with this event.
+     */
+    public float getY() {
+        return mY;
+    }
+
+    /**
+     * Returns the pressure associated with this event. Returns {@link Float#NaN} if omitted.
+     */
+    public float getPressure() {
+        return mPressure;
+    }
+
+    /**
+     * Returns the major axis size associated with this event. Returns {@link Float#NaN} if omitted.
+     */
+    public float getMajorAxisSize() {
+        return mMajorAxisSize;
+    }
+
+    /**
+     * Builder for {@link VirtualTouchEvent}.
+     */
+    public static final class Builder {
+
+        private @ToolType int mToolType = TOOL_TYPE_UNKNOWN;
+        private int mPointerId = MotionEvent.INVALID_POINTER_ID;
+        private @Action int mAction = ACTION_UNKNOWN;
+        private float mX = Float.NaN;
+        private float mY = Float.NaN;
+        private float mPressure = Float.NaN;
+        private float mMajorAxisSize = Float.NaN;
+
+        /**
+         * Creates a {@link VirtualTouchEvent} object with the current builder configuration.
+         */
+        public @NonNull VirtualTouchEvent build() {
+            if (mToolType == TOOL_TYPE_UNKNOWN || mPointerId == MotionEvent.INVALID_POINTER_ID
+                    || mAction == ACTION_UNKNOWN || Float.isNaN(mX) || Float.isNaN(mY)) {
+                throw new IllegalArgumentException(
+                        "Cannot build virtual touch event with unset required fields");
+            }
+            if ((mToolType == TOOL_TYPE_PALM && mAction != ACTION_CANCEL)
+                    || (mAction == ACTION_CANCEL && mToolType != TOOL_TYPE_PALM)) {
+                throw new IllegalArgumentException(
+                        "ACTION_CANCEL and TOOL_TYPE_PALM must always appear together");
+            }
+            return new VirtualTouchEvent(mPointerId, mToolType, mAction, mX, mY, mPressure,
+                    mMajorAxisSize);
+        }
+
+        /**
+         * Sets the pointer id of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setPointerId(int pointerId) {
+            mPointerId = pointerId;
+            return this;
+        }
+
+        /**
+         * Sets the tool type of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setToolType(@ToolType int toolType) {
+            if (toolType != TOOL_TYPE_FINGER && toolType != TOOL_TYPE_PALM) {
+                throw new IllegalArgumentException("Unsupported touch event tool type");
+            }
+            mToolType = toolType;
+            return this;
+        }
+
+        /**
+         * Sets the action of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setAction(@Action int action) {
+            if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE
+                    && action != ACTION_CANCEL) {
+                throw new IllegalArgumentException("Unsupported touch event action type");
+            }
+            mAction = action;
+            return this;
+        }
+
+        /**
+         * Sets the x-axis location of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setX(float absX) {
+            mX = absX;
+            return this;
+        }
+
+        /**
+         * Sets the y-axis location of the event.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setY(float absY) {
+            mY = absY;
+            return this;
+        }
+
+        /**
+         * Sets the pressure of the event. This field is optional and can be omitted.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setPressure(@FloatRange(from = 0f) float pressure) {
+            if (pressure < 0f) {
+                throw new IllegalArgumentException("Touch event pressure cannot be negative");
+            }
+            mPressure = pressure;
+            return this;
+        }
+
+        /**
+         * Sets the major axis size of the event. This field is optional and can be omitted.
+         *
+         * @return this builder, to allow for chaining of calls
+         */
+        public @NonNull Builder setMajorAxisSize(@FloatRange(from = 0f) float majorAxisSize) {
+            if (majorAxisSize < 0f) {
+                throw new IllegalArgumentException(
+                        "Touch event major axis size cannot be negative");
+            }
+            mMajorAxisSize = majorAxisSize;
+            return this;
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<VirtualTouchEvent> CREATOR =
+            new Parcelable.Creator<VirtualTouchEvent>() {
+        public VirtualTouchEvent createFromParcel(Parcel source) {
+            return new VirtualTouchEvent(source);
+        }
+        public VirtualTouchEvent[] newArray(int size) {
+            return new VirtualTouchEvent[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java
new file mode 100644
index 0000000..c8d602a
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualTouchscreen.java
@@ -0,0 +1,71 @@
+/*
+ * 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.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.Closeable;
+
+/**
+ * A virtual touchscreen representing a touch-based display input mechanism on a remote device.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualTouchscreen implements Closeable {
+
+    private final IVirtualDevice mVirtualDevice;
+    private final IBinder mToken;
+
+    /** @hide */
+    public VirtualTouchscreen(IVirtualDevice virtualDevice, IBinder token) {
+        mVirtualDevice = virtualDevice;
+        mToken = token;
+    }
+
+    @Override
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void close() {
+        try {
+            mVirtualDevice.unregisterInputDevice(mToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sends a touch event to the system.
+     *
+     * @param event the event to send
+     */
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
+        try {
+            mVirtualDevice.sendTouchEvent(mToken, event);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index bcdd519..a525f58 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -60,6 +60,8 @@
      */
     private final boolean mPersistent;
 
+    private Integer mId = null;
+
     /* package */ ContextHubClient(ContextHubInfo hubInfo, boolean persistent) {
         mAttachedHub = hubInfo;
         mPersistent = persistent;
@@ -85,6 +87,11 @@
         }
 
         mClientProxy = clientProxy;
+        try {
+            mId = Integer.valueOf(mClientProxy.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -98,6 +105,31 @@
     }
 
     /**
+     * Returns the system-wide unique identifier for this ContextHubClient.
+     *
+     * This value can be used as an identifier for the messaging channel between a
+     * ContextHubClient and the Context Hub. This may be used as a routing mechanism
+     * between various ContextHubClient objects within an application.
+     *
+     * The value returned by this method will remain the same if it is associated with
+     * the same client reference at the ContextHubService (for instance, the ID of a
+     * PendingIntent ContextHubClient will remain the same even if the local object
+     * has been regenerated with the equivalent PendingIntent). If the ContextHubClient
+     * is newly generated (e.g. any regeneration of a callback client, or generation
+     * of a non-equal PendingIntent client), the ID will not be the same.
+     *
+     * @return The ID of this ContextHubClient.
+     *
+     * @throws IllegalStateException if the ID was not set internally.
+     */
+    public int getId() {
+        if (mId == null) {
+            throw new IllegalStateException("ID was not set");
+        }
+        return mId;
+    }
+
+    /**
      * Closes the connection for this client and the Context Hub Service.
      *
      * When this function is invoked, the messaging associated with this client is invalidated.
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
index e33545c..2423a58 100644
--- a/core/java/android/hardware/location/IContextHubClient.aidl
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -29,4 +29,7 @@
 
     // Closes the connection with the Context Hub
     void close();
+
+    // Returns the unique ID for this client.
+    int getId();
 }
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 8a0211c..70fe5d6 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,11 +16,19 @@
 
 package android.net;
 
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_CARRIER;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
+import android.util.Log;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -42,10 +50,25 @@
  * @hide
  */
 public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
+    private static final String TAG = NetworkPolicy.class.getSimpleName();
     private static final int VERSION_INIT = 1;
     private static final int VERSION_RULE = 2;
     private static final int VERSION_RAPID = 3;
 
+    /**
+     * Initial Version of the NetworkTemplate backup serializer.
+     */
+    private static final int TEMPLATE_BACKUP_VERSION_1_INIT = 1;
+    /**
+     * Version of the NetworkTemplate backup serializer that added carrier template support.
+     */
+    private static final int TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
+    /**
+     * Latest Version of the NetworkTemplate Backup Serializer.
+     */
+    private static final int TEMPLATE_BACKUP_VERSION_LATEST =
+            TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
+
     public static final int CYCLE_NONE = -1;
     public static final long WARNING_DISABLED = -1;
     public static final long LIMIT_DISABLED = -1;
@@ -255,7 +278,7 @@
         DataOutputStream out = new DataOutputStream(baos);
 
         out.writeInt(VERSION_RAPID);
-        out.write(template.getBytesForBackup());
+        out.write(getNetworkTemplateBytesForBackup());
         cycleRule.writeToStream(out);
         out.writeLong(warningBytes);
         out.writeLong(limitBytes);
@@ -274,7 +297,7 @@
             throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
         }
 
-        final NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+        final NetworkTemplate template = getNetworkTemplateFromBackup(in);
         final RecurrenceRule cycleRule;
         if (version >= VERSION_RULE) {
             cycleRule = new RecurrenceRule(in);
@@ -298,4 +321,61 @@
         return new NetworkPolicy(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze,
                 lastLimitSnooze, lastRapidSnooze, metered, inferred);
     }
+
+    @NonNull
+    private byte[] getNetworkTemplateBytesForBackup() throws IOException {
+        if (!template.isPersistable()) {
+            Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
+        }
+
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final DataOutputStream out = new DataOutputStream(baos);
+
+        out.writeInt(TEMPLATE_BACKUP_VERSION_LATEST);
+
+        out.writeInt(template.getMatchRule());
+        BackupUtils.writeString(out, template.getSubscriberId());
+        BackupUtils.writeString(out, template.getNetworkId());
+        out.writeInt(template.getMeteredness());
+        out.writeInt(template.getSubscriberIdMatchRule());
+
+        return baos.toByteArray();
+    }
+
+    @NonNull
+    private static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
+            throws IOException, BackupUtils.BadVersionException {
+        int version = in.readInt();
+        if (version < TEMPLATE_BACKUP_VERSION_1_INIT || version > TEMPLATE_BACKUP_VERSION_LATEST) {
+            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
+        }
+
+        int matchRule = in.readInt();
+        final String subscriberId = BackupUtils.readString(in);
+        final String networkId = BackupUtils.readString(in);
+
+        final int metered;
+        final int subscriberIdMatchRule;
+        if (version >= TEMPLATE_BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
+            metered = in.readInt();
+            subscriberIdMatchRule = in.readInt();
+        } else {
+            // For backward compatibility, fill the missing filters from match rules.
+            metered = (matchRule == MATCH_MOBILE
+                    || matchRule == NetworkTemplate.MATCH_MOBILE_WILDCARD
+                    || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
+            subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
+        }
+
+        try {
+            return new NetworkTemplate(matchRule,
+                    subscriberId, new String[]{subscriberId},
+                    networkId, metered, NetworkStats.ROAMING_ALL,
+                    NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
+                    NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
+        } catch (IllegalArgumentException e) {
+            throw new BackupUtils.BadVersionException(
+                    "Restored network template contains unknown match rule " + matchRule, e);
+        }
+    }
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 4d5f97c..09eac79 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4210,8 +4210,7 @@
      * trying to instantiate an element.
      */
     @Nullable
-    public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
-            @NonNull Class<T> clazz) {
+    public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
         Objects.requireNonNull(clazz);
         return readParcelableInternal(loader, clazz);
     }
@@ -4222,10 +4221,6 @@
     @SuppressWarnings("unchecked")
     @Nullable
     private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
-        if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
-            throw new BadParcelableException("About to unparcel a parcelable object "
-                    + " but class required " + clazz.getName() + " is not Parcelable");
-        }
         Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
         if (creator == null) {
             return null;
@@ -4461,8 +4456,7 @@
      * deserializing the object.
      */
     @Nullable
-    public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
-            @NonNull Class<T> clazz) {
+    public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
         Objects.requireNonNull(clazz);
         return readSerializableInternal(
                 loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4474,11 +4468,6 @@
     @Nullable
     private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
             @Nullable Class<T> clazz) {
-        if (clazz != null && !Serializable.class.isAssignableFrom(clazz)) {
-            throw new BadParcelableException("About to unparcel a serializable object "
-                    + " but class required " + clazz.getName() + " is not Serializable");
-        }
-
         String name = readString();
         if (name == null) {
             // For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 29d4d78..563d6cc3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2073,7 +2073,8 @@
     @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.CREATE_USERS})
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS})
     @UserHandleAware
     public @NonNull String getUserType() {
         UserInfo userInfo = getUserInfo(mUserId);
@@ -2082,15 +2083,21 @@
 
     /**
      * Returns the user name of the context user. This call is only available to applications on
-     * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code
-     * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions.
+     * the system image.
      *
      * @return the user name
      */
-    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED,
-            android.Manifest.permission.CREATE_USERS}, conditional = true)
-    @UserHandleAware
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.CREATE_USERS,
+                    android.Manifest.permission.QUERY_USERS})
     public @NonNull String getUserName() {
         if (UserHandle.myUserId() == mUserId) {
             try {
@@ -2153,8 +2160,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public boolean isPrimaryUser() {
         final UserInfo user = getUserInfo(getContextUserIfAppropriate());
@@ -2184,7 +2193,8 @@
     @SystemApi
     @RequiresPermission(anyOf = {
             Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public boolean isAdminUser() {
         return isUserAdmin(getContextUserIfAppropriate());
@@ -2196,8 +2206,10 @@
      * user.
      */
     @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     public boolean isUserAdmin(@UserIdInt int userId) {
         UserInfo user = getUserInfo(userId);
         return user != null && user.isAdmin();
@@ -2394,8 +2406,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     @UserHandleAware
     public @Nullable UserHandle getRestrictedProfileParent() {
         final UserInfo info = getUserInfo(mUserId);
@@ -2412,8 +2426,10 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     public boolean isGuestUser(@UserIdInt int userId) {
         UserInfo user = getUserInfo(userId);
         return user != null && user.isGuest();
@@ -2426,8 +2442,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public boolean isGuestUser() {
         UserInfo user = getUserInfo(getContextUserIfAppropriate());
@@ -2456,17 +2474,15 @@
     /**
      * Checks if the calling context user is running in a profile.
      *
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the
-     * caller must be in the same profile group of specified user.
-     *
      * @return whether the caller is in a profile.
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     public boolean isProfile() {
         return isProfile(mUserId);
     }
@@ -2502,8 +2518,8 @@
             enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
             requiresAnyOfPermissionsIfNotCallerProfileGroup = {
                     android.Manifest.permission.MANAGE_USERS,
-                    android.Manifest.permission.INTERACT_ACROSS_USERS}
-    )
+                    android.Manifest.permission.QUERY_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     public boolean isManagedProfile() {
         return isManagedProfile(getContextUserIfAppropriate());
     }
@@ -2511,15 +2527,18 @@
     /**
      * Checks if the specified user is a managed profile.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} permission, otherwise the caller
      * must be in the same profile group of specified user.
      *
      * @return whether the specified user is a managed profile.
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean isManagedProfile(@UserIdInt int userId) {
         if (userId == mUserId) {
             // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
@@ -2573,8 +2592,10 @@
      * @return whether the context user is an ephemeral user.
      * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     @UserHandleAware
     public boolean isEphemeralUser() {
         return isUserEphemeral(mUserId);
@@ -2584,8 +2605,10 @@
      * Returns whether the specified user is ephemeral.
      * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     public boolean isUserEphemeral(@UserIdInt int userId) {
         final UserInfo user = getUserInfo(userId);
         return user != null && user.isEphemeral();
@@ -2853,8 +2876,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS})
     public UserInfo getUserInfo(@UserIdInt int userId) {
         try {
             return mService.getUserInfo(userId);
@@ -2877,7 +2902,9 @@
     @Deprecated
     @SystemApi
     @UserRestrictionSource
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.QUERY_USERS})
     public int getUserRestrictionSource(@UserRestrictionKey String restrictionKey,
             UserHandle userHandle) {
         try {
@@ -2896,7 +2923,9 @@
      * @return a list of user ids enforcing this restriction.
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
     public List<EnforcingUser> getUserRestrictionSources(
             @UserRestrictionKey String restrictionKey, UserHandle userHandle) {
         try {
@@ -3963,15 +3992,17 @@
     }
 
     /**
-     * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
-     * permission.
+     * Checks whether it's possible to add more managed profiles.
      * if allowedToRemoveOne is true and if the user already has a managed profile, then return if
      * we could add a new managed profile to this user after removing the existing one.
      *
      * @return true if more managed profiles can be added, false if limit has been reached.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
     public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
         try {
             return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
@@ -3987,7 +4018,10 @@
      * @return true if more profiles can be added, false if limit has been reached.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
     public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
         try {
             return mService.canAddMoreProfilesToUser(userType, userId, false);
@@ -4022,14 +4056,17 @@
      * <p>Note that this includes all profile types (not including Restricted profiles).
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
+     * {@link android.Manifest.permission#CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
      * @param userId profiles of this user will be returned.
      * @return the list of profiles.
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
     public List<UserInfo> getProfiles(@UserIdInt int userId) {
         try {
             return mService.getProfiles(userId, false /* enabledOnly */);
@@ -4048,7 +4085,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
     public boolean isSameProfileGroup(@NonNull UserHandle user, @NonNull UserHandle otherUser) {
         return isSameProfileGroup(user.getIdentifier(), otherUser.getIdentifier());
     }
@@ -4060,7 +4099,9 @@
      * @return true if the two user ids are in the same profile group.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.QUERY_USERS})
     public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
         try {
             return mService.isSameProfileGroup(userId, otherUserId);
@@ -4075,14 +4116,20 @@
      * <p>Note that this includes all profile types (not including Restricted profiles).
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
+     * {@link android.Manifest.permission#CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS} if userId is not the calling user.
      * @param userId profiles of this user will be returned.
      * @return the list of profiles.
+     * @deprecated use {@link #getUserProfiles()} instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
     public List<UserInfo> getEnabledProfiles(@UserIdInt int userId) {
         try {
             return mService.getProfiles(userId, true /* enabledOnly */);
@@ -4102,8 +4149,8 @@
             enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
             requiresAnyOfPermissionsIfNotCaller = {
                     Manifest.permission.MANAGE_USERS,
-                    Manifest.permission.CREATE_USERS}
-    )
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
     public List<UserHandle> getUserProfiles() {
         int[] userIds = getProfileIds(getContextUserIfAppropriate(), true /* enabledOnly */);
         return convertUserIdsToUserHandles(userIds);
@@ -4118,9 +4165,11 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
     public @NonNull List<UserHandle> getEnabledProfiles() {
         return getProfiles(true);
     }
@@ -4134,9 +4183,11 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
     public @NonNull List<UserHandle> getAllProfiles() {
         return getProfiles(false);
     }
@@ -4149,9 +4200,11 @@
      * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
      * @return A non-empty list of UserHandles associated with the context user.
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
-    @UserHandleAware
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.CREATE_USERS,
+                    Manifest.permission.QUERY_USERS})
     private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) {
         final int[] userIds = getProfileIds(mUserId, enabledOnly);
         return convertUserIdsToUserHandles(userIds);
@@ -4177,8 +4230,10 @@
      *
      * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
     public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
         try {
             return mService.getProfileIds(userId, enabledOnly);
@@ -4192,8 +4247,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
     public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
         return getProfileIds(userId, false /* enabledOnly */);
     }
@@ -4202,8 +4259,10 @@
      * @see #getProfileIds(int, boolean)
      * @hide
      */
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS}, conditional = true)
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS,
+            Manifest.permission.QUERY_USERS}, conditional = true)
     public int[] getEnabledProfileIds(@UserIdInt int userId) {
         return getProfileIds(userId, true /* enabledOnly */);
     }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 21c1feb..22b9578 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -671,6 +671,14 @@
     @TestApi
     public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
 
+    /**
+     * Namespace for all ultra wideband (uwb) related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_UWB = "uwb";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 65a857e..e64d685 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1749,8 +1749,9 @@
     /**
      * Called when there has been a failure transferring the {@link AssistStructure} to
      * the assistant.  This may happen, for example, if the data is too large and results
-     * in an out of memory exception, or the client has provided corrupt data.  This will
-     * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
+     * in an out of memory exception, the data has been cleared during transferring due to
+     * the new incoming assist data, or the client has provided corrupt data. This will be
+     * called immediately before {@link #onHandleAssist} and the AssistStructure supplied
      * there afterwards will be null.
      *
      * @param failure The failure exception that was thrown when building the
@@ -1788,7 +1789,8 @@
      * Called to receive data from the application that the user was currently viewing when
      * an assist session is started. If the original show request did not specify
      * {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide
-     * {@link ActivityId}.
+     * {@link ActivityId}. If there was a failure to write the assist data to
+     * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null.
      *
      * <p>This method is called for all activities along with an index and count that indicates
      * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
@@ -2213,7 +2215,8 @@
          * @return If available, the structure definition of all windows currently
          * displayed by the app. May be null if assist data has been disabled by the user
          * or device policy; will be null if the original show request did not specify
-         * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist
+         * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to
+         * {@link AssistStructure}; will be an empty stub if the application has disabled assist
          * by marking its window as secure.
          */
         public @Nullable AssistStructure getAssistStructure() {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index cb1cff9..859fd80 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -247,8 +247,8 @@
             } else if (listener.mSubId != null) {
                 subId = listener.mSubId;
             }
-            sRegistry.listenWithEventList(
-                    subId, pkg, featureId, listener.callback, eventsList, notifyNow);
+            sRegistry.listenWithEventList(false, false, subId, pkg, featureId,
+                    listener.callback, eventsList, notifyNow);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -263,11 +263,13 @@
      * @param events List events
      * @param notifyNow Whether to notify instantly
      */
-    private void listenFromCallback(int subId, @NonNull String pkg, @NonNull String featureId,
+    private void listenFromCallback(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess, int subId,
+            @NonNull String pkg, @NonNull String featureId,
             @NonNull TelephonyCallback telephonyCallback, @NonNull int[] events,
             boolean notifyNow) {
         try {
-            sRegistry.listenWithEventList(
+            sRegistry.listenWithEventList(renounceFineLocationAccess, renounceCoarseLocationAccess,
                     subId, pkg, featureId, telephonyCallback.callback, events, notifyNow);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1161,14 +1163,17 @@
      *
      * @param callback The {@link TelephonyCallback} object to register.
      */
-    public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerTelephonyCallback(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess,
+            @NonNull @CallbackExecutor Executor executor,
             int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback,
             boolean notifyNow) {
         if (callback == null) {
             throw new IllegalStateException("telephony service is null.");
         }
         callback.init(executor);
-        listenFromCallback(subId, pkgName, attributionTag, callback,
+        listenFromCallback(renounceFineLocationAccess, renounceCoarseLocationAccess, subId,
+                pkgName, attributionTag, callback,
                 getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow);
     }
 
@@ -1179,6 +1184,7 @@
      */
     public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag,
             @NonNull TelephonyCallback callback, boolean notifyNow) {
-        listenFromCallback(subId, pkgName, attributionTag, callback, new int[0], notifyNow);
+        listenFromCallback(false, false, subId,
+                pkgName, attributionTag, callback, new int[0], notifyNow);
     }
 }
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index b8e50fc..adb8b86 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1495,6 +1495,15 @@
      */
     public static final int TOOL_TYPE_ERASER = 4;
 
+    /**
+     * Tool type constant: The tool is a palm and should be rejected.
+     *
+     * @see #getToolType
+     *
+     * @hide
+     */
+    public static final int TOOL_TYPE_PALM = 5;
+
     // NOTE: If you add a new tool type here you must also add it to:
     //  native/include/android/input.h
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 75d5ecf..70505fc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4910,13 +4910,14 @@
         }
     }
 
-    private void updateColorModeIfNeeded(int colorMode) {
+    private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode) {
         if (mAttachInfo.mThreadedRenderer == null) {
             return;
         }
         // TODO: Centralize this sanitization? Why do we let setting bad modes?
         // Alternatively, can we just let HWUI figure it out? Do we need to care here?
-        if (!getConfiguration().isScreenWideColorGamut()) {
+        if (colorMode != ActivityInfo.COLOR_MODE_A8
+                && !getConfiguration().isScreenWideColorGamut()) {
             colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         }
         mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 357dbc0..2702c2d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -437,7 +437,10 @@
                     if (view.getViewTranslationResponse() != null
                             && view.getViewTranslationResponse().equals(response)) {
                         if (callback instanceof TextViewTranslationCallback) {
-                            if (((TextViewTranslationCallback) callback).isShowingTranslation()) {
+                            TextViewTranslationCallback textViewCallback =
+                                    (TextViewTranslationCallback) callback;
+                            if (textViewCallback.isShowingTranslation()
+                                    || textViewCallback.isAnimationRunning()) {
                                 if (DEBUG) {
                                     Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
                                             + ". Ignoring.");
@@ -623,7 +626,7 @@
 
     private void addViewIfNeeded(IntArray sourceViewIds, View view) {
         final AutofillId autofillId = view.getAutofillId();
-        if ((sourceViewIds.indexOf(autofillId.getViewId()) >= 0)
+        if (autofillId != null && (sourceViewIds.indexOf(autofillId.getViewId()) >= 0)
                 && !mViews.containsKey(autofillId)) {
             mViews.put(autofillId, new WeakReference<>(view));
         }
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 4a78f3e..942be21 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -46,6 +46,7 @@
 
     private TranslationTransformationMethod mTranslationTransformation;
     private boolean mIsShowingTranslation = false;
+    private boolean mAnimationRunning = false;
     private boolean mIsTextPaddingEnabled = false;
     private CharSequence mPaddedText;
     private int mAnimationDurationMillis = 250; // default value
@@ -92,6 +93,7 @@
                 (TextView) view,
                 () -> {
                     mIsShowingTranslation = true;
+                    mAnimationRunning = false;
                     // TODO(b/178353965): well-handle setTransformationMethod.
                     ((TextView) view).setTransformationMethod(transformation);
                 });
@@ -124,6 +126,7 @@
                     (TextView) view,
                     () -> {
                         mIsShowingTranslation = false;
+                        mAnimationRunning = false;
                         ((TextView) view).setTransformationMethod(transformation);
                     });
             if (!TextUtils.isEmpty(mContentDescription)) {
@@ -162,6 +165,13 @@
         return mIsShowingTranslation;
     }
 
+    /**
+     * Returns whether the view is running animation to show or hide the translation.
+     */
+    public boolean isAnimationRunning() {
+        return mAnimationRunning;
+    }
+
     @Override
     public void enableContentPadding() {
         mIsTextPaddingEnabled = true;
@@ -230,6 +240,7 @@
             mAnimator.end();
             // Note: mAnimator is now null; do not use again here.
         }
+        mAnimationRunning = true;
         int fadedOutColor = colorWithAlpha(view.getCurrentTextColor(), 0);
         mAnimator = ValueAnimator.ofArgb(view.getCurrentTextColor(), fadedOutColor);
         mAnimator.addUpdateListener(
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index dbf7eb3..2bf2f319 100644
--- a/core/java/android/window/PictureInPictureSurfaceTransaction.java
+++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Matrix;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,9 +35,10 @@
  * @hide
  */
 public final class PictureInPictureSurfaceTransaction implements Parcelable {
+    private static final float NOT_SET = -1f;
 
-    public final float mPositionX;
-    public final float mPositionY;
+    public final float mAlpha;
+    public final PointF mPosition;
 
     public final float[] mFloat9;
 
@@ -45,33 +47,37 @@
 
     public final float mCornerRadius;
 
-    private final Rect mWindowCrop = new Rect();
+    private final Rect mWindowCrop;
 
-    public PictureInPictureSurfaceTransaction(Parcel in) {
-        mPositionX = in.readFloat();
-        mPositionY = in.readFloat();
+    private PictureInPictureSurfaceTransaction(Parcel in) {
+        mAlpha = in.readFloat();
+        mPosition = in.readTypedObject(PointF.CREATOR);
         mFloat9 = new float[9];
         in.readFloatArray(mFloat9);
         mRotation = in.readFloat();
         mCornerRadius = in.readFloat();
-        mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
+        mWindowCrop = in.readTypedObject(Rect.CREATOR);
     }
 
-    public PictureInPictureSurfaceTransaction(float positionX, float positionY,
-            float[] float9, float rotation, float cornerRadius,
+    private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
+            @Nullable float[] float9, float rotation, float cornerRadius,
             @Nullable Rect windowCrop) {
-        mPositionX = positionX;
-        mPositionY = positionY;
-        mFloat9 = Arrays.copyOf(float9, 9);
-        mRotation = rotation;
-        mCornerRadius = cornerRadius;
-        if (windowCrop != null) {
-            mWindowCrop.set(windowCrop);
+        mAlpha = alpha;
+        mPosition = position;
+        if (float9 == null) {
+            mFloat9 = new float[9];
+            Matrix.IDENTITY_MATRIX.getValues(mFloat9);
+            mRotation = 0;
+        } else {
+            mFloat9 = Arrays.copyOf(float9, 9);
+            mRotation = rotation;
         }
+        mCornerRadius = cornerRadius;
+        mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
     }
 
     public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
-        this(other.mPositionX, other.mPositionY,
+        this(other.mAlpha, other.mPosition,
                 other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop);
     }
 
@@ -82,13 +88,18 @@
         return matrix;
     }
 
+    /** @return {@code true} if this transaction contains setting corner radius. */
+    public boolean hasCornerRadiusSet() {
+        return mCornerRadius > 0;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
         PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
-        return Objects.equals(mPositionX, that.mPositionX)
-                && Objects.equals(mPositionY, that.mPositionY)
+        return Objects.equals(mAlpha, that.mAlpha)
+                && Objects.equals(mPosition, that.mPosition)
                 && Arrays.equals(mFloat9, that.mFloat9)
                 && Objects.equals(mRotation, that.mRotation)
                 && Objects.equals(mCornerRadius, that.mCornerRadius)
@@ -97,7 +108,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9),
+        return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
                 mRotation, mCornerRadius, mWindowCrop);
     }
 
@@ -108,8 +119,8 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeFloat(mPositionX);
-        out.writeFloat(mPositionY);
+        out.writeFloat(mAlpha);
+        out.writeTypedObject(mPosition, 0 /* flags */);
         out.writeFloatArray(mFloat9);
         out.writeFloat(mRotation);
         out.writeFloat(mCornerRadius);
@@ -120,8 +131,8 @@
     public String toString() {
         final Matrix matrix = getMatrix();
         return "PictureInPictureSurfaceTransaction("
-                + " posX=" + mPositionX
-                + " posY=" + mPositionY
+                + " alpha=" + mAlpha
+                + " position=" + mPosition
                 + " matrix=" + matrix.toShortString()
                 + " rotation=" + mRotation
                 + " cornerRadius=" + mCornerRadius
@@ -134,11 +145,20 @@
             @NonNull SurfaceControl surfaceControl,
             @NonNull SurfaceControl.Transaction tx) {
         final Matrix matrix = surfaceTransaction.getMatrix();
-        tx.setMatrix(surfaceControl, matrix, new float[9])
-                .setPosition(surfaceControl,
-                        surfaceTransaction.mPositionX, surfaceTransaction.mPositionY)
-                .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop)
-                .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+        tx.setMatrix(surfaceControl, matrix, new float[9]);
+        if (surfaceTransaction.mPosition != null) {
+            tx.setPosition(surfaceControl,
+                    surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
+        }
+        if (surfaceTransaction.mWindowCrop != null) {
+            tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
+        }
+        if (surfaceTransaction.hasCornerRadiusSet()) {
+            tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+        }
+        if (surfaceTransaction.mAlpha != NOT_SET) {
+            tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
+        }
     }
 
     public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
@@ -151,4 +171,44 @@
                     return new PictureInPictureSurfaceTransaction[size];
                 }
             };
+
+    public static class Builder {
+        private float mAlpha = NOT_SET;
+        private PointF mPosition;
+        private float[] mFloat9;
+        private float mRotation;
+        private float mCornerRadius = NOT_SET;
+        private Rect mWindowCrop;
+
+        public Builder setAlpha(float alpha) {
+            mAlpha = alpha;
+            return this;
+        }
+
+        public Builder setPosition(float x, float y) {
+            mPosition = new PointF(x, y);
+            return this;
+        }
+
+        public Builder setTransform(@NonNull float[] float9, float rotation) {
+            mFloat9 = Arrays.copyOf(float9, 9);
+            mRotation = rotation;
+            return this;
+        }
+
+        public Builder setCornerRadius(float cornerRadius) {
+            mCornerRadius = cornerRadius;
+            return this;
+        }
+
+        public Builder setWindowCrop(@NonNull Rect windowCrop) {
+            mWindowCrop = new Rect(windowCrop);
+            return this;
+        }
+
+        public PictureInPictureSurfaceTransaction build() {
+            return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
+                    mFloat9, mRotation, mCornerRadius, mWindowCrop);
+        }
+    }
 }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 5e75797..4745220e 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -23,6 +23,7 @@
 import android.app.WindowConfiguration;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -435,6 +436,21 @@
     }
 
     /**
+     * Starts activity(s) from a shortcut.
+     * @param callingPackage The package launching the shortcut.
+     * @param shortcutInfo Information about the shortcut to start
+     * @param options bundle containing ActivityOptions for the task's top activity.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
+            @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+        mHierarchyOps.add(HierarchyOp.createForStartShortcut(
+                callingPackage, shortcutInfo, options));
+        return this;
+    }
+
+    /**
      * Creates a new TaskFragment with the given options.
      * @param taskFragmentOptions the options used to create the TaskFragment.
      */
@@ -957,11 +973,16 @@
         public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
         public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
         public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
+        public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
         public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
 
+        // When starting from a shortcut, this contains the calling package.
+        public static final String LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE =
+                "android:transaction.hop.shortcut_calling_package";
+
         private final int mType;
 
         // Container we are performing the operation on.
@@ -999,6 +1020,9 @@
         @Nullable
         private PendingIntent mPendingIntent;
 
+        @Nullable
+        private ShortcutInfo mShortcutInfo;
+
         public static HierarchyOp createForReparent(
                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1058,6 +1082,17 @@
                     .build();
         }
 
+        /** Create a hierarchy op for starting a shortcut. */
+        public static HierarchyOp createForStartShortcut(@NonNull String callingPackage,
+                @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+            final Bundle fullOptions = options == null ? new Bundle() : options;
+            fullOptions.putString(LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE, callingPackage);
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_START_SHORTCUT)
+                    .setShortcutInfo(shortcutInfo)
+                    .setLaunchOptions(fullOptions)
+                    .build();
+        }
+
         /** Create a hierarchy op for setting launch adjacent flag root. */
         public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
                 boolean clearRoot) {
@@ -1085,6 +1120,7 @@
             mActivityIntent = copy.mActivityIntent;
             mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
             mPendingIntent = copy.mPendingIntent;
+            mShortcutInfo = copy.mShortcutInfo;
         }
 
         protected HierarchyOp(Parcel in) {
@@ -1100,6 +1136,7 @@
             mActivityIntent = in.readTypedObject(Intent.CREATOR);
             mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
             mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+            mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
         }
 
         public int getType() {
@@ -1170,6 +1207,11 @@
             return mPendingIntent;
         }
 
+        @Nullable
+        public ShortcutInfo getShortcutInfo() {
+            return mShortcutInfo;
+        }
+
         @Override
         public String toString() {
             switch (mType) {
@@ -1212,6 +1254,9 @@
                 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
                     return "{SetAdjacentTaskFragments: container=" + mContainer
                             + " adjacentContainer=" + mReparent + "}";
+                case HIERARCHY_OP_TYPE_START_SHORTCUT:
+                    return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
+                            + "}";
                 default:
                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                             + " mToTop=" + mToTop
@@ -1234,6 +1279,7 @@
             dest.writeTypedObject(mActivityIntent, flags);
             dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
             dest.writeTypedObject(mPendingIntent, flags);
+            dest.writeTypedObject(mShortcutInfo, flags);
         }
 
         @Override
@@ -1287,6 +1333,9 @@
             @Nullable
             private PendingIntent mPendingIntent;
 
+            @Nullable
+            private ShortcutInfo mShortcutInfo;
+
             Builder(int type) {
                 mType = type;
             }
@@ -1347,6 +1396,11 @@
                 return this;
             }
 
+            Builder setShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
+                mShortcutInfo = shortcutInfo;
+                return this;
+            }
+
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
                 hierarchyOp.mContainer = mContainer;
@@ -1364,6 +1418,7 @@
                 hierarchyOp.mActivityIntent = mActivityIntent;
                 hierarchyOp.mPendingIntent = mPendingIntent;
                 hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+                hierarchyOp.mShortcutInfo = mShortcutInfo;
 
                 return hierarchyOp;
             }
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index b331a9e..4ba7ef2 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,7 +15,6 @@
  */
 package android.window;
 
-import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
 import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
 import static android.window.ConfigurationHelper.isDifferentDisplay;
 import static android.window.ConfigurationHelper.shouldUpdateResources;
@@ -222,14 +221,7 @@
                         () -> windowContext.dispatchConfigurationChanged(newConfig));
             }
 
-            // Dispatch onConfigurationChanged only if there's a significant public change to
-            // make it compatible with the original behavior.
-            final Configuration[] sizeConfigurations = context.getResources()
-                    .getSizeConfigurations();
-            final SizeConfigurationBuckets buckets = sizeConfigurations != null
-                    ? new SizeConfigurationBuckets(sizeConfigurations) : null;
-            final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
-
+            final int diff = mConfiguration.diffPublicOnly(newConfig);
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 52122ee..bc3c2f5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -171,9 +171,6 @@
     private AppPredictor mWorkAppPredictor;
     private boolean mShouldDisplayLandscape;
 
-    private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
-    private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
-
     @UnsupportedAppUsage
     public ChooserActivity() {
     }
@@ -294,6 +291,7 @@
 
     private int mCurrAvailableWidth = 0;
     private int mLastNumberOfChildren = -1;
+    private int mMaxTargetsPerRow = 1;
 
     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
 
@@ -681,8 +679,9 @@
             mCallerChooserTargets = targets;
         }
 
-        mShouldDisplayLandscape = shouldDisplayLandscape(
-                getResources().getConfiguration().orientation);
+        mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+        mShouldDisplayLandscape =
+                shouldDisplayLandscape(getResources().getConfiguration().orientation);
         setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
@@ -915,7 +914,7 @@
                 adapter,
                 getPersonalProfileUserHandle(),
                 /* workProfileUserHandle= */ null,
-                isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+                isSendAction(getTargetIntent()), mMaxTargetsPerRow);
     }
 
     private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -944,7 +943,7 @@
                 selectedProfile,
                 getPersonalProfileUserHandle(),
                 getWorkProfileUserHandle(),
-                isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+                isSendAction(getTargetIntent()), mMaxTargetsPerRow);
     }
 
     private int findSelectedProfile() {
@@ -1112,6 +1111,7 @@
         }
 
         mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
+        mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
         adjustPreviewWidth(newConfig.orientation, null);
         updateStickyContentPreview();
     }
@@ -2560,7 +2560,7 @@
                 // and b/150936654
                 recyclerView.setAdapter(gridAdapter);
                 ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
-                        getMaxTargetsPerRow());
+                        mMaxTargetsPerRow);
             }
 
             UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
@@ -2724,7 +2724,7 @@
 
     @Override // ChooserListCommunicator
     public int getMaxRankedTargets() {
-        return getMaxTargetsPerRow();
+        return mMaxTargetsPerRow;
     }
 
     @Override // ChooserListCommunicator
@@ -3075,13 +3075,6 @@
         }
     }
 
-    int getMaxTargetsPerRow() {
-        int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
-        if (mShouldDisplayLandscape) {
-            maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
-        }
-        return maxTargets;
-    }
     /**
      * Adapter for all types of items and targets in ShareSheet.
      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3149,7 +3142,11 @@
                 return false;
             }
 
-            int newWidth = width / getMaxTargetsPerRow();
+            // Limit width to the maximum width of the chooser activity
+            int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width);
+            width = Math.min(maxWidth, width);
+
+            int newWidth = width / mMaxTargetsPerRow;
             if (newWidth != mChooserTargetWidth) {
                 mChooserTargetWidth = newWidth;
                 return true;
@@ -3184,7 +3181,7 @@
                             + getAzLabelRowCount()
                             + Math.ceil(
                             (float) mChooserListAdapter.getAlphaTargetCount()
-                                    / getMaxTargetsPerRow())
+                                    / mMaxTargetsPerRow)
             );
         }
 
@@ -3224,7 +3221,7 @@
         public int getCallerAndRankedTargetRowCount() {
             return (int) Math.ceil(
                     ((float) mChooserListAdapter.getCallerTargetCount()
-                            + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow());
+                            + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
         }
 
         // There can be at most one row in the listview, that is internally
@@ -3423,7 +3420,7 @@
                 parentGroup.addView(row2);
 
                 mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
-                        Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType,
+                        Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType,
                         mChooserMultiProfilePagerAdapter::getActiveListAdapter);
                 loadViewsIntoGroup(mDirectShareViewHolder);
 
@@ -3432,7 +3429,7 @@
                 ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
                         false);
                 ItemGroupViewHolder holder =
-                        new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
+                        new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
                 loadViewsIntoGroup(holder);
 
                 return holder;
@@ -3524,7 +3521,7 @@
             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
             final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
             if (position < serviceRows) {
-                return position * getMaxTargetsPerRow();
+                return position * mMaxTargetsPerRow;
             }
 
             position -= serviceRows;
@@ -3533,7 +3530,7 @@
                                                  + mChooserListAdapter.getRankedTargetCount();
             final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
             if (position < callerAndRankedRows) {
-                return serviceCount + position * getMaxTargetsPerRow();
+                return serviceCount + position * mMaxTargetsPerRow;
             }
 
             position -= getAzLabelRowCount() + callerAndRankedRows;
@@ -3546,7 +3543,7 @@
             if (mDirectShareViewHolder != null && canExpandDirectShare) {
                 mDirectShareViewHolder.handleScroll(
                         mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
-                        getMaxTargetsPerRow());
+                        mMaxTargetsPerRow);
             }
         }
 
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index a0a0f32..6ba0279 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -43,7 +43,8 @@
     void removeOnSubscriptionsChangedListener(String pkg,
             IOnSubscriptionsChangedListener callback);
 
-    void listenWithEventList(in int subId, String pkg, String featureId,
+    void listenWithEventList(in boolean renounceFineLocationAccess,
+            in boolean renounceCoarseLocationAccess, in int subId, String pkg, String featureId,
             IPhoneStateListener callback, in int[] events, boolean notifyNow);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void notifyCallStateForAllSubs(int state, String incomingNumber);
diff --git a/core/java/com/android/internal/util/AnnotationValidations.java b/core/java/com/android/internal/util/AnnotationValidations.java
index cf5e48f..e1b3802 100644
--- a/core/java/com/android/internal/util/AnnotationValidations.java
+++ b/core/java/com/android/internal/util/AnnotationValidations.java
@@ -26,7 +26,7 @@
 import android.annotation.UserIdInt;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.PackageInfoFlagsBits;
 import android.content.pm.PackageManager.PermissionResult;
 import android.os.UserHandle;
 
@@ -160,8 +160,8 @@
     }
 
     public static void validate(
-            Class<PackageInfoFlags> annotation, PackageInfoFlags ignored, int value) {
-        validateIntFlags(annotation, value,
+            Class<PackageInfoFlagsBits> annotation, PackageInfoFlagsBits ignored, long value) {
+        validateLongFlags(annotation, value,
                 flagsUpTo(PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS));
     }
 
@@ -212,7 +212,12 @@
             invalid(annotation, "0x" + Integer.toHexString(value));
         }
     }
-
+    private static void validateLongFlags(
+            Class<? extends Annotation> annotation, long value, int validBits) {
+        if ((validBits & value) != validBits) {
+            invalid(annotation, "0x" + Long.toHexString(value));
+        }
+    }
     private static void invalid(Class<? extends Annotation> annotation, Object value) {
         invalid("@" + annotation.getSimpleName(), value);
     }
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index e6deada..78bb53d 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -871,6 +871,10 @@
             if (newGroup == null) {
                 newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
                 mAddedGroups.add(newGroup);
+            } else if (newGroup.getParent() != mMessagingLinearLayout) {
+                throw new IllegalStateException(
+                        "group parent was " + newGroup.getParent() + " but expected "
+                                + mMessagingLinearLayout);
             }
             newGroup.setImageDisplayLocation(mIsCollapsed
                     ? IMAGE_DISPLAY_LOCATION_EXTERNAL
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index f30b844..9e06e33 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -32,7 +32,6 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
-import android.util.Pools;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -57,8 +56,8 @@
  */
 @RemoteViews.RemoteView
 public class MessagingGroup extends LinearLayout implements MessagingLinearLayout.MessagingChild {
-    private static Pools.SimplePool<MessagingGroup> sInstancePool
-            = new Pools.SynchronizedPool<>(10);
+    private static final MessagingPool<MessagingGroup> sInstancePool =
+            new MessagingPool<>(10);
 
     /**
      * Images are displayed inline.
@@ -338,7 +337,7 @@
     }
 
     public static void dropCache() {
-        sInstancePool = new Pools.SynchronizedPool<>(10);
+        sInstancePool.clear();
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 27689d4..f7955c3 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -28,7 +28,6 @@
 import android.net.Uri;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Pools;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -44,8 +43,8 @@
 @RemoteViews.RemoteView
 public class MessagingImageMessage extends ImageView implements MessagingMessage {
     private static final String TAG = "MessagingImageMessage";
-    private static Pools.SimplePool<MessagingImageMessage> sInstancePool
-            = new Pools.SynchronizedPool<>(10);
+    private static final MessagingPool<MessagingImageMessage> sInstancePool =
+            new MessagingPool<>(10);
     private final MessagingMessageState mState = new MessagingMessageState(this);
     private final int mMinImageHeight;
     private final Path mPath = new Path();
@@ -194,7 +193,7 @@
     }
 
     public static void dropCache() {
-        sInstancePool = new Pools.SynchronizedPool<>(10);
+        sInstancePool.clear();
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index e1602a9..21ca196 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -419,6 +419,10 @@
             if (newGroup == null) {
                 newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
                 mAddedGroups.add(newGroup);
+            } else if (newGroup.getParent() != mMessagingLinearLayout) {
+                throw new IllegalStateException(
+                        "group parent was " + newGroup.getParent() + " but expected "
+                                + mMessagingLinearLayout);
             }
             newGroup.setImageDisplayLocation(mIsCollapsed
                     ? IMAGE_DISPLAY_LOCATION_EXTERNAL
diff --git a/core/java/com/android/internal/widget/MessagingPool.java b/core/java/com/android/internal/widget/MessagingPool.java
new file mode 100644
index 0000000..9c0fe4b
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingPool.java
@@ -0,0 +1,73 @@
+/*
+ * 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.util.Log;
+import android.util.Pools;
+import android.view.View;
+
+/**
+ * A trivial wrapper around Pools.SynchronizedPool which allows clearing the pool, as well as
+ * disabling the pool class altogether.
+ * @param <T> the type of object in the pool
+ */
+public class MessagingPool<T extends View> implements Pools.Pool<T> {
+    private static final boolean ENABLED = false;  // disabled to test b/208508846
+    private static final String TAG = "MessagingPool";
+    private final int mMaxPoolSize;
+    private Pools.SynchronizedPool<T> mCurrentPool;
+
+    public MessagingPool(int maxPoolSize) {
+        mMaxPoolSize = maxPoolSize;
+        if (ENABLED) {
+            mCurrentPool = new Pools.SynchronizedPool<>(mMaxPoolSize);
+        }
+    }
+
+    @Override
+    public T acquire() {
+        if (!ENABLED) {
+            return null;
+        }
+        T instance = mCurrentPool.acquire();
+        if (instance.getParent() != null) {
+            Log.wtf(TAG, "acquired " + instance + " with parent " + instance.getParent());
+            return null;
+        }
+        return instance;
+    }
+
+    @Override
+    public boolean release(T instance) {
+        if (instance.getParent() != null) {
+            Log.wtf(TAG, "releasing " + instance + " with parent " + instance.getParent());
+            return false;
+        }
+        if (!ENABLED) {
+            return false;
+        }
+        return mCurrentPool.release(instance);
+    }
+
+    /** Clear the pool */
+    public void clear() {
+        if (ENABLED) {
+            mCurrentPool = new Pools.SynchronizedPool<>(mMaxPoolSize);
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
index d778c59..19791db 100644
--- a/core/java/com/android/internal/widget/MessagingTextMessage.java
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.text.Layout;
 import android.util.AttributeSet;
-import android.util.Pools;
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
@@ -36,8 +35,8 @@
 @RemoteViews.RemoteView
 public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
 
-    private static Pools.SimplePool<MessagingTextMessage> sInstancePool
-            = new Pools.SynchronizedPool<>(20);
+    private static final MessagingPool<MessagingTextMessage> sInstancePool =
+            new MessagingPool<>(20);
     private final MessagingMessageState mState = new MessagingMessageState(this);
 
     public MessagingTextMessage(@NonNull Context context) {
@@ -92,7 +91,7 @@
     }
 
     public static void dropCache() {
-        sInstancePool = new Pools.SynchronizedPool<>(10);
+        sInstancePool.clear();
     }
 
     @Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7018df2..4c36732 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2791,6 +2791,11 @@
     <permission android:name="android.permission.CREATE_USERS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows an application to call APIs that allow it to query users on the
+         device. -->
+    <permission android:name="android.permission.QUERY_USERS"
+                android:protectionLevel="signature|role" />
+
     <!-- Allows an application to access data blobs across users. -->
     <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
         android:protectionLevel="signature|privileged|development|role" />
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 90caacc..933b4d2 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,8 +20,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_gravity="center"
     android:maxCollapsedHeight="0dp"
     android:maxCollapsedHeightSmall="56dp"
+    android:maxWidth="@dimen/chooser_width"
     android:id="@id/contentPanel">
 
     <RelativeLayout
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 0d04d7f3..52692b0 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -34,7 +34,7 @@
     <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
           android:id="@+id/content_preview_image_1_large"
           android:layout_width="120dp"
-          android:layout_height="140dp"
+          android:layout_height="104dp"
           android:layout_alignParentTop="true"
           android:adjustViewBounds="true"
           android:gravity="center"
@@ -44,7 +44,7 @@
           android:id="@+id/content_preview_image_2_large"
           android:visibility="gone"
           android:layout_width="120dp"
-          android:layout_height="140dp"
+          android:layout_height="104dp"
           android:layout_alignParentTop="true"
           android:layout_toRightOf="@id/content_preview_image_1_large"
           android:layout_marginLeft="10dp"
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54..d686dd2 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,5 +51,7 @@
 
     <!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
     <bool name="config_showUserSwitcherByDefault">true</bool>
+
+    <integer name="config_chooser_max_targets_per_row">6</integer>
 </resources>
 
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 02ed848..e8f15fd 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -110,4 +110,7 @@
     <dimen name="immersive_mode_cling_width">380dp</dimen>
 
     <dimen name="floating_toolbar_preferred_width">544dp</dimen>
+
+    <dimen name="chooser_width">624dp</dimen>
+
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 91e4074..0a6ef7c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2808,6 +2808,22 @@
         <attr name="attributionTags" />
     </declare-styleable>
 
+    <!-- @hide The <code>apex-system-service</code> tag declares an apex system service
+         that is contained within an application.
+
+         The apex system service tag appears as a child tag of the
+         {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestApexSystemService"
+                       parent="AndroidManifestApplication">
+        <!-- The fully qualified class name of the system service. -->
+        <attr name="name" />
+        <!-- The filepath to the .jar that contains the system service. If this is not provided, it
+             is assumed that the system service exists in SYSTEMSERVERCLASSPATH. -->
+        <attr name="path" />
+        <attr name="minSdkVersion" />
+        <attr name="maxSdkVersion" />
+    </declare-styleable>
+
     <!-- The <code>receiver</code> tag declares an
          {@link android.content.BroadcastReceiver} class that is available
          as part of the package's application components, allowing the
@@ -3051,6 +3067,7 @@
     <declare-styleable name="AndroidManifestMetaData"
          parent="AndroidManifestApplication
                  AndroidManifestActivity
+                 AndroidManifestApexSystemService
                  AndroidManifestReceiver
                  AndroidManifestProvider
                  AndroidManifestService
@@ -3085,6 +3102,7 @@
     <declare-styleable name="AndroidManifestProperty"
          parent="AndroidManifestApplication
                  AndroidManifestActivity
+                 AndroidManifestApexSystemService
                  AndroidManifestReceiver
                  AndroidManifestProvider
                  AndroidManifestService">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 480c679..ddec469 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2383,7 +2383,7 @@
     <!-- If supported and enabled, are dreams activated when asleep and charging? (by default) -->
     <bool name="config_dreamsActivatedOnSleepByDefault">false</bool>
     <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
-    <string name="config_dreamsDefaultComponent" translatable="false">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
+    <string name="config_dreamsDefaultComponent" translatable="false">com.android.deskclock/com.android.deskclock.Screensaver</string>
 
     <!-- Are we allowed to dream while not plugged in? -->
     <bool name="config_dreamsEnabledOnBattery">false</bool>
@@ -5516,4 +5516,6 @@
         <!-- Add configurations here, example: -->
         <!-- <item>SUPL_HOST=supl.google.com</item> -->
     </string-array>
+
+    <integer name="config_chooser_max_targets_per_row">4</integer>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c40ec4f..a877bd3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -899,7 +899,8 @@
     <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
 
     <!-- chooser/resolver (sharesheet) spacing -->
-    <dimen name="chooser_corner_radius">16dp</dimen>
+    <dimen name="chooser_width">412dp</dimen>
+    <dimen name="chooser_corner_radius">28dp</dimen>
     <dimen name="chooser_row_text_option_translate">25dp</dimen>
     <dimen name="chooser_view_spacing">18dp</dimen>
     <dimen name="chooser_edge_margin_thin">16dp</dimen>
@@ -916,7 +917,7 @@
     <dimen name="resolver_icon_size">32dp</dimen>
     <dimen name="resolver_button_bar_spacing">0dp</dimen>
     <dimen name="resolver_badge_size">18dp</dimen>
-    <dimen name="resolver_icon_margin">16dp</dimen>
+    <dimen name="resolver_icon_margin">8dp</dimen>
     <dimen name="resolver_small_margin">18dp</dimen>
     <dimen name="resolver_edge_margin">24dp</dimen>
     <dimen name="resolver_elevation">1dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 134235d..55bf24b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
   <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
   <java-symbol type="bool" name="config_avoidGfxAccel" />
   <java-symbol type="bool" name="config_bluetooth_address_validation" />
+  <java-symbol type="integer" name="config_chooser_max_targets_per_row" />
   <java-symbol type="bool" name="config_flipToScreenOffEnabled" />
   <java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
@@ -2861,6 +2862,7 @@
   <java-symbol type="layout" name="date_picker_month_item_material" />
   <java-symbol type="id" name="month_view" />
   <java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
+  <java-symbol type="dimen" name="chooser_width" />
   <java-symbol type="dimen" name="chooser_corner_radius" />
   <java-symbol type="string" name="chooser_no_direct_share_targets" />
   <java-symbol type="drawable" name="chooser_row_layer_list" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1c41e00..32d72b37 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -47,6 +47,7 @@
         "androidx.test.ext.junit",
         "androidx.test.runner",
         "androidx.test.rules",
+        "kotlin-test",
         "mockito-target-minus-junit4",
         "ub-uiautomator",
         "platform-test-annotations",
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java
new file mode 100644
index 0000000..37cc9b7
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.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 android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import android.view.KeyEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualKeyEventTest {
+
+    @Test
+    public void keyEvent_emptyBuilder() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualKeyEvent.Builder().build());
+    }
+
+    @Test
+    public void keyEvent_noKeyCode() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new VirtualKeyEvent.Builder().setAction(VirtualKeyEvent.ACTION_DOWN).build());
+    }
+
+    @Test
+    public void keyEvent_noAction() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new VirtualKeyEvent.Builder().setKeyCode(KeyEvent.KEYCODE_A).build());
+    }
+
+    @Test
+    public void keyEvent_created() {
+        final VirtualKeyEvent event = new VirtualKeyEvent.Builder()
+                .setAction(VirtualKeyEvent.ACTION_DOWN)
+                .setKeyCode(KeyEvent.KEYCODE_A).build();
+        assertWithMessage("Incorrect key code").that(event.getKeyCode()).isEqualTo(
+                KeyEvent.KEYCODE_A);
+        assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+                VirtualKeyEvent.ACTION_DOWN);
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java
new file mode 100644
index 0000000..789e0bb
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.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 android.hardware.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseButtonEventTest {
+
+    @Test
+    public void buttonEvent_emptyBuilder() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new VirtualMouseButtonEvent.Builder().build());
+    }
+
+    @Test
+    public void buttonEvent_noButtonCode() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+                .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE).build());
+    }
+
+    @Test
+    public void buttonEvent_noAction() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+                .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build());
+    }
+
+    @Test
+    public void buttonEvent_created() {
+        final VirtualMouseButtonEvent event = new VirtualMouseButtonEvent.Builder()
+                .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+                .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build();
+        assertWithMessage("Incorrect button code").that(event.getButtonCode()).isEqualTo(
+                VirtualMouseButtonEvent.BUTTON_BACK);
+        assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+                VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java
new file mode 100644
index 0000000..c050816
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseRelativeEventTest {
+
+    @Test
+    public void relativeEvent_created() {
+        final VirtualMouseRelativeEvent event = new VirtualMouseRelativeEvent.Builder()
+                .setRelativeX(-5f)
+                .setRelativeY(8f).build();
+        assertWithMessage("Incorrect x value").that(event.getRelativeX()).isEqualTo(-5f);
+        assertWithMessage("Incorrect y value").that(event.getRelativeY()).isEqualTo(8f);
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java
new file mode 100644
index 0000000..2259c74
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseScrollEventTest {
+
+    @Test
+    public void scrollEvent_xOutOfRange() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+                .setXAxisMovement(1.5f)
+                .setYAxisMovement(1.0f));
+    }
+
+    @Test
+    public void scrollEvent_yOutOfRange() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+                .setXAxisMovement(0.5f)
+                .setYAxisMovement(1.1f));
+    }
+
+    @Test
+    public void scrollEvent_created() {
+        final VirtualMouseScrollEvent event = new VirtualMouseScrollEvent.Builder()
+                .setXAxisMovement(-1f)
+                .setYAxisMovement(1f).build();
+        assertWithMessage("Incorrect x value").that(event.getXAxisMovement()).isEqualTo(-1f);
+        assertWithMessage("Incorrect y value").that(event.getYAxisMovement()).isEqualTo(1f);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java b/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java
new file mode 100644
index 0000000..3f504a0
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.input;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualTouchEventTest {
+
+    @Test
+    public void touchEvent_emptyBuilder() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder().build());
+    }
+
+    @Test
+    public void touchEvent_noAction() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_noPointerId() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setY(1f)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_noToolType() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_noX() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setY(1f)
+                .setPointerId(1)
+                .build());
+    }
+
+
+    @Test
+    public void touchEvent_noY() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setPointerId(1)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_created() {
+        final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .build();
+        assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+                VirtualTouchEvent.ACTION_DOWN);
+        assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+                VirtualTouchEvent.TOOL_TYPE_FINGER);
+        assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+        assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+        assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+    }
+
+    @Test
+    public void touchEvent_created_withPressureAndAxis() {
+        final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_DOWN)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .setPressure(0.5f)
+                .setMajorAxisSize(10f)
+                .build();
+        assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+                VirtualTouchEvent.ACTION_DOWN);
+        assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+                VirtualTouchEvent.TOOL_TYPE_FINGER);
+        assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+        assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+        assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+        assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+        assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+                10f);
+    }
+
+    @Test
+    public void touchEvent_cancelUsedImproperly() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_CANCEL)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_palmUsedImproperly() {
+        assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_MOVE)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .build());
+    }
+
+    @Test
+    public void touchEvent_palmAndCancelUsedProperly() {
+        final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+                .setAction(VirtualTouchEvent.ACTION_CANCEL)
+                .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+                .setX(0f)
+                .setY(1f)
+                .setPointerId(1)
+                .setPressure(0.5f)
+                .setMajorAxisSize(10f)
+                .build();
+        assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+                VirtualTouchEvent.ACTION_CANCEL);
+        assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+                VirtualTouchEvent.TOOL_TYPE_PALM);
+        assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+        assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+        assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+        assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+        assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+                10f);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
new file mode 100644
index 0000000..d936cad
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.net
+
+import android.text.format.Time.TIMEZONE_UTC
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.ByteArrayInputStream
+import java.io.DataInputStream
+import java.time.ZoneId
+import kotlin.test.assertEquals
+
+private const val TEST_IMSI1 = "TESTIMSI1"
+private const val TEST_SSID1 = "TESTISSID1"
+
+@RunWith(AndroidJUnit4::class)
+class NetworkPolicyTest {
+    @Test
+    fun testTemplateBackupRestore() {
+        assertPolicyBackupRestore(createTestPolicyForTemplate(
+                NetworkTemplate.buildTemplateWifi(TEST_SSID1)))
+        assertPolicyBackupRestore(createTestPolicyForTemplate(
+                NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
+        assertPolicyBackupRestore(createTestPolicyForTemplate(
+                NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI1)))
+    }
+
+    private fun createTestPolicyForTemplate(template: NetworkTemplate): NetworkPolicy {
+        return NetworkPolicy(template, NetworkPolicy.buildRule(5, ZoneId.of(TIMEZONE_UTC)),
+                NetworkPolicy.WARNING_DISABLED, NetworkPolicy.LIMIT_DISABLED,
+                NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER,
+                /*metered*/ false, /*inferred*/ true)
+    }
+
+    private fun assertPolicyBackupRestore(policy: NetworkPolicy) {
+        val bytes = policy.bytesForBackup
+        val stream = DataInputStream(ByteArrayInputStream(bytes))
+        val restored = NetworkPolicy.getNetworkPolicyFromBackup(stream)
+        assertEquals(policy, restored)
+    }
+}
\ No newline at end of file
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 756425e..0b8dc3f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,6 +180,7 @@
     <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
     <assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
     <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
+    <assign-permission name="android.permission.REAL_GET_TASKS" uid="cameraserver" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index af19bd0..b8e8b01 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -295,11 +295,12 @@
             @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
             @NonNull TaskFragmentContainer secondaryContainer,
             @NonNull SplitRule splitRule) {
+        SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+                secondaryContainer, splitRule);
+        // Remove container later to prevent pinning escaping toast showing in lock task mode.
         if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
             removeExistingSecondaryContainers(wct, primaryContainer);
         }
-        SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
-                secondaryContainer, splitRule);
         mSplitContainers.add(splitContainer);
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 81be21c..ade5731 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -112,8 +112,7 @@
         secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
 
         // Set adjacent to each other so that the containers below will be invisible.
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
 
         mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
 
@@ -149,8 +148,7 @@
                 secondaryActivity, secondaryRectBounds, primaryContainer);
 
         // Set adjacent to each other so that the containers below will be invisible.
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
 
         mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
 
@@ -269,8 +267,22 @@
         final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
         resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
 
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
+    }
+
+    private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer primaryContainer,
+            @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) {
+        final Rect parentBounds = getParentContainerBounds(primaryContainer);
+        // Clear adjacent TaskFragments if the container is shown in fullscreen, or the
+        // secondaryContainer could not be finished.
+        if (!shouldShowSideBySide(parentBounds, splitRule)) {
+            setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                    null /* secondary */, null /* splitRule */);
+        } else {
+            setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                    secondaryContainer.getTaskFragmentToken(), splitRule);
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
similarity index 85%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
index 22cd384..26848b1 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
@@ -16,6 +16,6 @@
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/size_compat_background"/>
-    <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+    <solid android:color="@color/compat_controls_background"/>
+    <corners android:radius="@dimen/compat_hint_corner_radius"/>
 </shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
similarity index 88%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
index af9063a..0e0ca37 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
@@ -15,11 +15,11 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/size_compat_hint_point_width"
+        android:width="@dimen/compat_hint_point_width"
         android:height="8dp"
         android:viewportWidth="10"
         android:viewportHeight="8">
     <path
-        android:fillColor="@color/size_compat_background"
+        android:fillColor="@color/compat_controls_background"
         android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 18caa35..ab74e43 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
         android:viewportWidth="48"
         android:viewportHeight="48">
     <path
-        android:fillColor="@color/size_compat_background"
+        android:fillColor="@color/compat_controls_background"
         android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
     <group
         android:translateX="12"
         android:translateY="12">
         <path
-            android:fillColor="@color/size_compat_text"
+            android:fillColor="@color/compat_controls_text"
             android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
         <path
-            android:fillColor="@color/size_compat_text"
+            android:fillColor="@color/compat_controls_text"
             android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
     </group>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
new file mode 100644
index 0000000..c04e258e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:clipToPadding="false"
+    android:paddingEnd="@dimen/compat_hint_padding_end"
+    android:paddingBottom="5dp"
+    android:clickable="true">
+
+    <TextView
+        android:id="@+id/compat_mode_hint_text"
+        android:layout_width="188dp"
+        android:layout_height="wrap_content"
+        android:lineSpacingExtra="4sp"
+        android:background="@drawable/compat_hint_bubble"
+        android:padding="16dp"
+        android:textAlignment="viewStart"
+        android:textColor="@color/compat_controls_text"
+        android:textSize="14sp"/>
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:src="@drawable/compat_hint_point"
+        android:paddingHorizontal="@dimen/compat_hint_corner_radius"
+        android:contentDescription="@null"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
similarity index 82%
rename from libs/WindowManager/Shell/res/layout/size_compat_ui.xml
rename to libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 82ebee2..6f946b2 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -14,10 +14,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.wm.shell.sizecompatui.SizeCompatRestartButton
+<com.android.wm.shell.compatui.CompatUILayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="bottom|end">
+
+    <include android:id="@+id/size_compat_hint"
+         layout="@layout/compat_mode_hint"/>
 
     <FrameLayout
         android:layout_width="@dimen/size_compat_button_width"
@@ -36,4 +41,4 @@
 
     </FrameLayout>
 
-</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
+</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
deleted file mode 100644
index d0e7c42..0000000
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-    <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:clipToPadding="false"
-        android:paddingBottom="5dp">
-
-        <LinearLayout
-            android:id="@+id/size_compat_hint_popup"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:clickable="true">
-
-            <TextView
-                android:layout_width="188dp"
-                android:layout_height="wrap_content"
-                android:lineSpacingExtra="4sp"
-                android:background="@drawable/size_compat_hint_bubble"
-                android:padding="16dp"
-                android:text="@string/restart_button_description"
-                android:textAlignment="viewStart"
-                android:textColor="@color/size_compat_text"
-                android:textSize="14sp"/>
-
-            <ImageView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:src="@drawable/size_compat_hint_point"
-                android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
-                android:contentDescription="@null"/>
-
-        </LinearLayout>
-
-    </FrameLayout>
-
-</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 23a2172..cf596f7 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -30,9 +30,9 @@
     <color name="bubbles_dark">@color/GM2_grey_800</color>
     <color name="bubbles_icon_tint">@color/GM2_grey_700</color>
 
-    <!-- Size Compat Restart Button -->
-    <color name="size_compat_background">@android:color/system_neutral1_800</color>
-    <color name="size_compat_text">@android:color/system_neutral1_50</color>
+    <!-- Compat controls UI -->
+    <color name="compat_controls_background">@android:color/system_neutral1_800</color>
+    <color name="compat_controls_text">@android:color/system_neutral1_50</color>
 
     <!-- GM2 colors -->
     <color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9e77578..18e91f4 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -206,11 +206,15 @@
     <!-- The height of the size compat restart button including padding. -->
     <dimen name="size_compat_button_height">64dp</dimen>
 
-    <!-- The radius of the corners of the size compat hint bubble. -->
-    <dimen name="size_compat_hint_corner_radius">28dp</dimen>
+    <!-- The radius of the corners of the compat hint bubble. -->
+    <dimen name="compat_hint_corner_radius">28dp</dimen>
 
-    <!-- The width of the size compat hint point. -->
-    <dimen name="size_compat_hint_point_width">10dp</dimen>
+    <!-- The width of the compat hint point. -->
+    <dimen name="compat_hint_point_width">10dp</dimen>
+
+    <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
+         - compat_hint_corner_radius - compat_hint_point_width /2). -->
+    <dimen name="compat_hint_padding_end">7dp</dimen>
 
     <!-- The width of the brand image on staring surface. -->
     <dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8e98b82..8b3a356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -52,8 +52,8 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.startingsurface.StartingWindowController;
 
 import java.io.PrintWriter;
@@ -69,7 +69,7 @@
  * TODO(b/167582004): may consider consolidating this class and TaskOrganizer
  */
 public class ShellTaskOrganizer extends TaskOrganizer implements
-        SizeCompatUIController.SizeCompatUICallback {
+        CompatUIController.CompatUICallback {
 
     // Intentionally using negative numbers here so the positive numbers can be used
     // for task id specific listeners that will be added later.
@@ -98,9 +98,9 @@
         default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
         default void onTaskVanished(RunningTaskInfo taskInfo) {}
         default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
-        /** Whether this task listener supports size compat UI. */
-        default boolean supportSizeCompatUI() {
-            // All TaskListeners should support size compat except PIP.
+        /** Whether this task listener supports  compat UI. */
+        default boolean supportCompatUI() {
+            // All TaskListeners should support compat UI except PIP.
             return true;
         }
         /** Attaches the a child window surface to the task surface. */
@@ -159,11 +159,11 @@
     private StartingWindowController mStartingWindow;
 
     /**
-     * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+     * In charge of showing compat UI. Can be {@code null} if device doesn't support size
      * compat.
      */
     @Nullable
-    private final SizeCompatUIController mSizeCompatUI;
+    private final CompatUIController mCompatUI;
 
     @Nullable
     private final Optional<RecentTasksController> mRecentTasks;
@@ -172,32 +172,32 @@
     private RunningTaskInfo mLastFocusedTaskInfo;
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
-        this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+        this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
                 Optional.empty() /* recentTasksController */);
     }
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
-            SizeCompatUIController sizeCompatUI) {
-        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+            CompatUIController compatUI) {
+        this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
                 Optional.empty() /* recentTasksController */);
     }
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
-            SizeCompatUIController sizeCompatUI,
+            CompatUIController compatUI,
             Optional<RecentTasksController> recentTasks) {
-        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+        this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
                 recentTasks);
     }
 
     @VisibleForTesting
     ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
-            Context context, @Nullable SizeCompatUIController sizeCompatUI,
+            Context context, @Nullable CompatUIController compatUI,
             Optional<RecentTasksController> recentTasks) {
         super(taskOrganizerController, mainExecutor);
-        mSizeCompatUI = sizeCompatUI;
+        mCompatUI = compatUI;
         mRecentTasks = recentTasks;
-        if (sizeCompatUI != null) {
-            sizeCompatUI.setSizeCompatUICallback(this);
+        if (compatUI != null) {
+            compatUI.setCompatUICallback(this);
         }
     }
 
@@ -428,7 +428,7 @@
             listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
         }
         notifyLocusVisibilityIfNeeded(info.getTaskInfo());
-        notifySizeCompatUI(info.getTaskInfo(), listener);
+        notifyCompatUI(info.getTaskInfo(), listener);
     }
 
     /**
@@ -459,8 +459,8 @@
             }
             notifyLocusVisibilityIfNeeded(taskInfo);
             if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
-                // Notify the size compat UI if the listener or task info changed.
-                notifySizeCompatUI(taskInfo, newListener);
+                // Notify the compat UI if the listener or task info changed.
+                notifyCompatUI(taskInfo, newListener);
             }
             if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
                 // Notify the recent tasks when a task changes windowing modes
@@ -504,8 +504,8 @@
                 listener.onTaskVanished(taskInfo);
             }
             notifyLocusVisibilityIfNeeded(taskInfo);
-            // Pass null for listener to remove the size compat UI on this task if there is any.
-            notifySizeCompatUI(taskInfo, null /* taskListener */);
+            // Pass null for listener to remove the compat UI on this task if there is any.
+            notifyCompatUI(taskInfo, null /* taskListener */);
             // Notify the recent tasks that a task has been removed
             mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
         }
@@ -618,28 +618,28 @@
     }
 
     /**
-     * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+     * Notifies {@link CompatUIController} about the compat info changed on the give Task
      * to update the UI accordingly.
      *
      * @param taskInfo the new Task info
      * @param taskListener listener to handle the Task Surface placement. {@code null} if task is
      *                     vanished.
      */
-    private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
-        if (mSizeCompatUI == null) {
+    private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+        if (mCompatUI == null) {
             return;
         }
 
-        // The task is vanished or doesn't support size compat UI, notify to remove size compat UI
+        // The task is vanished or doesn't support compat UI, notify to remove compat UI
         // on this Task if there is any.
-        if (taskListener == null || !taskListener.supportSizeCompatUI()
+        if (taskListener == null || !taskListener.supportCompatUI()
                 || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
-            mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+            mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
                     null /* taskConfig */, null /* taskListener */);
             return;
         }
 
-        mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+        mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
                 taskInfo.configuration, taskListener);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 2f3214d..54e743f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -41,6 +41,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -77,6 +78,7 @@
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Executor mShellExecutor;
     private final SyncTransactionQueue mSyncQueue;
+    private final TaskViewTransitions mTaskViewTransitions;
 
     private ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mTaskToken;
@@ -92,17 +94,27 @@
     private final Rect mTmpRootRect = new Rect();
     private final int[] mTmpLocation = new int[2];
 
-    public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+    public TaskView(Context context, ShellTaskOrganizer organizer,
+            TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
         super(context, null, 0, 0, true /* disableBackgroundLayer */);
 
         mTaskOrganizer = organizer;
         mShellExecutor = organizer.getExecutor();
         mSyncQueue = syncQueue;
+        mTaskViewTransitions = taskViewTransitions;
+        if (mTaskViewTransitions != null) {
+            mTaskViewTransitions.addTaskView(this);
+        }
         setUseAlpha();
         getHolder().addCallback(this);
         mGuard.open("release");
     }
 
+    /** Until all users are converted, we may have mixed-use (eg. Car). */
+    private boolean isUsingShellTransitions() {
+        return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS;
+    }
+
     /**
      * Only one listener may be set on the view, throws an exception otherwise.
      */
@@ -129,6 +141,14 @@
             @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
         prepareActivityOptions(options, launchBounds);
         LauncherApps service = mContext.getSystemService(LauncherApps.class);
+        if (isUsingShellTransitions()) {
+            mShellExecutor.execute(() -> {
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
+                mTaskViewTransitions.startTaskView(wct, this);
+            });
+            return;
+        }
         try {
             service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
         } catch (Exception e) {
@@ -148,6 +168,14 @@
     public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
             @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
         prepareActivityOptions(options, launchBounds);
+        if (isUsingShellTransitions()) {
+            mShellExecutor.execute(() -> {
+                WindowContainerTransaction wct = new WindowContainerTransaction();
+                wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
+                mTaskViewTransitions.startTaskView(wct, this);
+            });
+            return;
+        }
         try {
             pendingIntent.send(mContext, 0 /* code */, fillInIntent,
                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
@@ -177,6 +205,16 @@
         mObscuredTouchRect = obscuredRect;
     }
 
+    private void onLocationChanged(WindowContainerTransaction wct) {
+        // Update based on the screen bounds
+        getBoundsOnScreen(mTmpRect);
+        getRootView().getBoundsOnScreen(mTmpRootRect);
+        if (!mTmpRootRect.contains(mTmpRect)) {
+            mTmpRect.offsetTo(0, 0);
+        }
+        wct.setBounds(mTaskToken, mTmpRect);
+    }
+
     /**
      * Call when view position or size has changed. Do not call when animating.
      */
@@ -184,15 +222,12 @@
         if (mTaskToken == null) {
             return;
         }
-        // Update based on the screen bounds
-        getBoundsOnScreen(mTmpRect);
-        getRootView().getBoundsOnScreen(mTmpRootRect);
-        if (!mTmpRootRect.contains(mTmpRect)) {
-            mTmpRect.offsetTo(0, 0);
-        }
+        // Sync Transactions can't operate simultaneously with shell transition collection.
+        // The transition animation (upon showing) will sync the location itself.
+        if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
 
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setBounds(mTaskToken, mTmpRect);
+        onLocationChanged(wct);
         mSyncQueue.queue(wct);
     }
 
@@ -217,6 +252,9 @@
 
     private void performRelease() {
         getHolder().removeCallback(this);
+        if (mTaskViewTransitions != null) {
+            mTaskViewTransitions.removeTaskView(this);
+        }
         mShellExecutor.execute(() -> {
             mTaskOrganizer.removeListener(this);
             resetTaskInfo();
@@ -254,6 +292,10 @@
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl leash) {
+        if (isUsingShellTransitions()) {
+            // Everything else handled by enter transition.
+            return;
+        }
         mTaskInfo = taskInfo;
         mTaskToken = taskInfo.token;
         mTaskLeash = leash;
@@ -288,6 +330,8 @@
 
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
+        // we know about -- so leave clean-up here even if shell transitions are enabled.
         if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
 
         if (mListener != null) {
@@ -355,6 +399,10 @@
                 // Nothing to update, task is not yet available
                 return;
             }
+            if (isUsingShellTransitions()) {
+                mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
+                return;
+            }
             // Reparent the task when this surface is created
             mTransaction.reparent(mTaskLeash, getSurfaceControl())
                     .show(mTaskLeash)
@@ -380,6 +428,11 @@
                 return;
             }
 
+            if (isUsingShellTransitions()) {
+                mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
+                return;
+            }
+
             // Unparent the task when this surface is destroyed
             mTransaction.reparent(mTaskLeash, null).apply();
             updateTaskVisibility();
@@ -421,4 +474,91 @@
         super.onDetachedFromWindow();
         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
     }
+
+    ActivityManager.RunningTaskInfo getTaskInfo() {
+        return mTaskInfo;
+    }
+
+    void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
+        if (mTaskToken == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+
+        finishTransaction.reparent(mTaskLeash, null).apply();
+
+        if (mListener != null) {
+            final int taskId = mTaskInfo.taskId;
+            mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+        }
+    }
+
+    /**
+     * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
+     * is used instead.
+     */
+    void prepareCloseAnimation() {
+        if (mTaskToken != null) {
+            if (mListener != null) {
+                final int taskId = mTaskInfo.taskId;
+                mListenerExecutor.execute(() -> {
+                    mListener.onTaskRemovalStarted(taskId);
+                });
+            }
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+        }
+        resetTaskInfo();
+    }
+
+    void prepareOpenAnimation(final boolean newTask,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+            WindowContainerTransaction wct) {
+        mTaskInfo = taskInfo;
+        mTaskToken = mTaskInfo.token;
+        mTaskLeash = leash;
+        if (mSurfaceCreated) {
+            // Surface is ready, so just reparent the task to this surface control
+            startTransaction.reparent(mTaskLeash, getSurfaceControl())
+                    .show(mTaskLeash)
+                    .apply();
+            // Also reparent on finishTransaction since the finishTransaction will reparent back
+            // to its "original" parent by default.
+            finishTransaction.reparent(mTaskLeash, getSurfaceControl())
+                    .setPosition(mTaskLeash, 0, 0)
+                    .apply();
+
+            // TODO: determine if this is really necessary or not
+            onLocationChanged(wct);
+        } else {
+            // The surface has already been destroyed before the task has appeared,
+            // so go ahead and hide the task entirely
+            wct.setHidden(mTaskToken, true /* hidden */);
+            // listener callback is below
+        }
+        if (newTask) {
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
+        }
+
+        if (mTaskInfo.taskDescription != null) {
+            int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
+            setResizeBackgroundColor(startTransaction, backgroundColor);
+        }
+
+        if (mListener != null) {
+            final int taskId = mTaskInfo.taskId;
+            final ComponentName baseActivity = mTaskInfo.baseActivity;
+
+            mListenerExecutor.execute(() -> {
+                if (newTask) {
+                    mListener.onTaskCreated(taskId, baseActivity);
+                }
+                // Even if newTask, send a visibilityChange if the surface was destroyed.
+                if (!newTask || !mSurfaceCreated) {
+                    mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+                }
+            });
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 8286d10..42844b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -31,13 +31,24 @@
     private final ShellTaskOrganizer mTaskOrganizer;
     private final ShellExecutor mShellExecutor;
     private final SyncTransactionQueue mSyncQueue;
+    private final TaskViewTransitions mTaskViewTransitions;
     private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
 
     public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
+            ShellExecutor shellExecutor, SyncTransactionQueue syncQueue,
+            TaskViewTransitions taskViewTransitions) {
+        mTaskOrganizer = taskOrganizer;
+        mShellExecutor = shellExecutor;
+        mSyncQueue = syncQueue;
+        mTaskViewTransitions = taskViewTransitions;
+    }
+
+    public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
             ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
         mTaskOrganizer = taskOrganizer;
         mShellExecutor = shellExecutor;
         mSyncQueue = syncQueue;
+        mTaskViewTransitions = null;
     }
 
     public TaskViewFactory asTaskViewFactory() {
@@ -46,7 +57,7 @@
 
     /** Creates an {@link TaskView} */
     public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
-        TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
+        TaskView taskView = new TaskView(context, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
         executor.execute(() -> {
             onCreate.accept(taskView);
         });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
new file mode 100644
index 0000000..83335ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -0,0 +1,253 @@
+/*
+ * 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;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+
+/**
+ * Handles Shell Transitions that involve TaskView tasks.
+ */
+public class TaskViewTransitions implements Transitions.TransitionHandler {
+    private static final String TAG = "TaskViewTransitions";
+
+    private final ArrayList<TaskView> mTaskViews = new ArrayList<>();
+    private final ArrayList<PendingTransition> mPending = new ArrayList<>();
+    private final Transitions mTransitions;
+    private final boolean[] mRegistered = new boolean[]{ false };
+
+    /**
+     * TaskView makes heavy use of startTransition. Only one shell-initiated transition can be
+     * in-flight (collecting) at a time (because otherwise, the operations could get merged into
+     * a single transition). So, keep a queue here until we add a queue in server-side.
+     */
+    private static class PendingTransition {
+        final @WindowManager.TransitionType int mType;
+        final WindowContainerTransaction mWct;
+        final @NonNull TaskView mTaskView;
+        IBinder mClaimed;
+
+        PendingTransition(@WindowManager.TransitionType int type,
+                @Nullable WindowContainerTransaction wct, @NonNull TaskView taskView) {
+            mType = type;
+            mWct = wct;
+            mTaskView = taskView;
+        }
+    }
+
+    public TaskViewTransitions(Transitions transitions) {
+        mTransitions = transitions;
+        // Defer registration until the first TaskView because we want this to be the "first" in
+        // priority when handling requests.
+        // TODO(210041388): register here once we have an explicit ordering mechanism.
+    }
+
+    void addTaskView(TaskView tv) {
+        synchronized (mRegistered) {
+            if (!mRegistered[0]) {
+                mRegistered[0] = true;
+                mTransitions.addHandler(this);
+            }
+        }
+        mTaskViews.add(tv);
+    }
+
+    void removeTaskView(TaskView tv) {
+        mTaskViews.remove(tv);
+        // Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
+    }
+
+    /**
+     * Looks through the pending transitions for one matching `taskView`.
+     * @param taskView the pending transition should be for this.
+     * @param closing When true, this only returns a pending transition of the close/hide type.
+     *                Otherwise it selects open/show.
+     * @param latest When true, this will only check the most-recent pending transition for the
+     *               specified taskView. If it doesn't match `closing`, this will return null even
+     *               if there is a match earlier. The idea behind this is to check the state of
+     *               the taskviews "as if all transitions already happened".
+     */
+    private PendingTransition findPending(TaskView taskView, boolean closing, boolean latest) {
+        for (int i = mPending.size() - 1; i >= 0; --i) {
+            if (mPending.get(i).mTaskView != taskView) continue;
+            if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
+                return mPending.get(i);
+            }
+            if (latest) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    private PendingTransition findPending(IBinder claimed) {
+        for (int i = 0; i < mPending.size(); ++i) {
+            if (mPending.get(i).mClaimed != claimed) continue;
+            return mPending.get(i);
+        }
+        return null;
+    }
+
+    /** @return whether there are pending transitions on TaskViews. */
+    public boolean hasPending() {
+        return !mPending.isEmpty();
+    }
+
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @Nullable TransitionRequestInfo request) {
+        final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+        if (triggerTask == null) {
+            return null;
+        }
+        final TaskView taskView = findTaskView(triggerTask);
+        if (taskView == null) return null;
+        // Opening types should all be initiated by shell
+        if (!Transitions.isClosingType(request.getType())) return null;
+        PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
+        if (pending == null) {
+            pending = new PendingTransition(request.getType(), null, taskView);
+        }
+        if (pending.mClaimed != null) {
+            throw new IllegalStateException("Task is closing in 2 collecting transitions?"
+                    + " This state doesn't make sense");
+        }
+        pending.mClaimed = transition;
+        return new WindowContainerTransaction();
+    }
+
+    private TaskView findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+        for (int i = 0; i < mTaskViews.size(); ++i) {
+            if (mTaskViews.get(i).getTaskInfo() == null) continue;
+            if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
+                return mTaskViews.get(i);
+            }
+        }
+        return null;
+    }
+
+    void startTaskView(WindowContainerTransaction wct, TaskView taskView) {
+        mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView));
+        startNextTransition();
+    }
+
+    void setTaskViewVisible(TaskView taskView, boolean visible) {
+        PendingTransition pending = findPending(taskView, !visible, true /* latest */);
+        if (pending != null) {
+            // Already opening or creating a task, so no need to do anything here.
+            return;
+        }
+        if (taskView.getTaskInfo() == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
+        pending = new PendingTransition(
+                visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView);
+        mPending.add(pending);
+        startNextTransition();
+        // visibility is reported in transition.
+    }
+
+    private void startNextTransition() {
+        if (mPending.isEmpty()) return;
+        final PendingTransition pending = mPending.get(0);
+        if (pending.mClaimed != null) {
+            // Wait for this to start animating.
+            return;
+        }
+        pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        PendingTransition pending = findPending(transition);
+        if (pending == null) return false;
+        mPending.remove(pending);
+        TaskView taskView = pending.mTaskView;
+        final ArrayList<TransitionInfo.Change> tasks = new ArrayList<>();
+        for (int i = 0; i < info.getChanges().size(); ++i) {
+            final TransitionInfo.Change chg = info.getChanges().get(i);
+            if (chg.getTaskInfo() == null) continue;
+            tasks.add(chg);
+        }
+        if (tasks.isEmpty()) {
+            Slog.e(TAG, "Got a TaskView transition with no task.");
+            return false;
+        }
+        WindowContainerTransaction wct = null;
+        for (int i = 0; i < tasks.size(); ++i) {
+            TransitionInfo.Change chg = tasks.get(i);
+            if (Transitions.isClosingType(chg.getMode())) {
+                final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
+                TaskView tv = findTaskView(chg.getTaskInfo());
+                if (tv == null) {
+                    throw new IllegalStateException("TaskView transition is closing a "
+                            + "non-taskview task ");
+                }
+                if (isHide) {
+                    tv.prepareHideAnimation(finishTransaction);
+                } else {
+                    tv.prepareCloseAnimation();
+                }
+            } else if (Transitions.isOpeningType(chg.getMode())) {
+                final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
+                if (wct == null) wct = new WindowContainerTransaction();
+                TaskView tv = taskView;
+                if (!taskIsNew) {
+                    tv = findTaskView(chg.getTaskInfo());
+                    if (tv == null) {
+                        throw new IllegalStateException("TaskView transition is showing a "
+                            + "non-taskview task ");
+                    }
+                }
+                tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction,
+                        chg.getTaskInfo(), chg.getLeash(), wct);
+            } else {
+                throw new IllegalStateException("Claimed transition isn't an opening or closing"
+                        + " type: " + chg.getMode());
+            }
+        }
+        // No animation, just show it immediately.
+        startTransaction.apply();
+        finishTransaction.apply();
+        finishCallback.onTransitionFinished(wct, null /* wctCB */);
+        startNextTransition();
+        return true;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ec59fad..f979f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -80,6 +80,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
@@ -136,6 +137,7 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final DisplayController mDisplayController;
+    private final TaskViewTransitions mTaskViewTransitions;
     private final SyncTransactionQueue mSyncQueue;
 
     // Used to post to main UI thread
@@ -212,6 +214,7 @@
             DisplayController displayController,
             ShellExecutor mainExecutor,
             Handler mainHandler,
+            TaskViewTransitions taskViewTransitions,
             SyncTransactionQueue syncQueue) {
         BubbleLogger logger = new BubbleLogger(uiEventLogger);
         BubblePositioner positioner = new BubblePositioner(context, windowManager);
@@ -220,7 +223,7 @@
                 new BubbleDataRepository(context, launcherApps, mainExecutor),
                 statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                 logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
-                mainHandler, syncQueue);
+                mainHandler, taskViewTransitions, syncQueue);
     }
 
     /**
@@ -243,6 +246,7 @@
             DisplayController displayController,
             ShellExecutor mainExecutor,
             Handler mainHandler,
+            TaskViewTransitions taskViewTransitions,
             SyncTransactionQueue syncQueue) {
         mContext = context;
         mLauncherApps = launcherApps;
@@ -266,6 +270,7 @@
         mSavedBubbleKeysPerUser = new SparseSetArray<>();
         mBubbleIconFactory = new BubbleIconFactory(context);
         mDisplayController = displayController;
+        mTaskViewTransitions = taskViewTransitions;
         mSyncQueue = syncQueue;
     }
 
@@ -570,6 +575,10 @@
         return mSyncQueue;
     }
 
+    TaskViewTransitions getTaskViewTransitions() {
+        return mTaskViewTransitions;
+    }
+
     /** Contains information to help position things on the screen.  */
     BubblePositioner getPositioner() {
         return mBubblePositioner;
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 a87aad4..af59062 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
@@ -335,7 +335,7 @@
             mManageButton.setVisibility(GONE);
         } else {
             mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
-                    mController.getSyncTransactionQueue());
+                    mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
             mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
             mExpandedViewContainer.addView(mTaskView);
             bringChildToFront(mTaskView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
similarity index 86%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index a703114..99dbfe0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import com.android.wm.shell.common.annotations.ExternalThread;
 
 /**
- * Interface to engage size compat UI.
+ * Interface to engage compat UI.
  */
 @ExternalThread
-public interface SizeCompatUI {
+public interface CompatUI {
     /**
-     * Called when the keyguard occluded state changes. Removes all size compat UIs if the
+     * Called when the keyguard occluded state changes. Removes all compat UIs if the
      * keyguard is now occluded.
      * @param occluded indicates if the keyguard is now occluded.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
similarity index 81%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e06070a..e0b2387 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -48,20 +48,20 @@
 
 /**
  * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
- * activities are in size compatibility mode.
+ * activities are in compatibility mode.
  */
-public class SizeCompatUIController implements OnDisplaysChangedListener,
+public class CompatUIController implements OnDisplaysChangedListener,
         DisplayImeController.ImePositionProcessor {
 
     /** Callback for size compat UI interaction. */
-    public interface SizeCompatUICallback {
+    public interface CompatUICallback {
         /** Called when the size compat restart button appears. */
         void onSizeCompatRestartButtonAppeared(int taskId);
         /** Called when the size compat restart button is clicked. */
         void onSizeCompatRestartButtonClicked(int taskId);
     }
 
-    private static final String TAG = "SizeCompatUIController";
+    private static final String TAG = "CompatUIController";
 
     /** Whether the IME is shown on display id. */
     private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
@@ -71,7 +71,7 @@
             new SparseArray<>(0);
 
     /** The showing UIs by task id. */
-    private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
+    private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
 
     /** Avoid creating display context frequently for non-default display. */
     private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@@ -82,17 +82,17 @@
     private final DisplayImeController mImeController;
     private final SyncTransactionQueue mSyncQueue;
     private final ShellExecutor mMainExecutor;
-    private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();
+    private final CompatUIImpl mImpl = new CompatUIImpl();
 
-    private SizeCompatUICallback mCallback;
+    private CompatUICallback mCallback;
 
     /** Only show once automatically in the process life. */
     private boolean mHasShownHint;
-    /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
+    /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
      * be shown. */
     private boolean mKeyguardOccluded;
 
-    public SizeCompatUIController(Context context,
+    public CompatUIController(Context context,
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
             DisplayImeController imeController,
@@ -108,35 +108,36 @@
         mImeController.addPositionProcessor(this);
     }
 
-    public SizeCompatUI asSizeCompatUI() {
+    /** Returns implementation of {@link CompatUI}. */
+    public CompatUI asCompatUI() {
         return mImpl;
     }
 
     /** Sets the callback for UI interactions. */
-    public void setSizeCompatUICallback(SizeCompatUICallback callback) {
+    public void setCompatUICallback(CompatUICallback callback) {
         mCallback = callback;
     }
 
     /**
-     * Called when the Task info changed. Creates and updates the size compat UI if there is an
+     * Called when the Task info changed. Creates and updates the compat UI if there is an
      * activity in size compat, or removes the UI if there is no size compat activity.
      *
      * @param displayId display the task and activity are in.
      * @param taskId task the activity is in.
-     * @param taskConfig task config to place the size compat UI with.
+     * @param taskConfig task config to place the compat UI with.
      * @param taskListener listener to handle the Task Surface placement.
      */
-    public void onSizeCompatInfoChanged(int displayId, int taskId,
+    public void onCompatInfoChanged(int displayId, int taskId,
             @Nullable Configuration taskConfig,
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
         if (taskConfig == null || taskListener == null) {
-            // Null token means the current foreground activity is not in size compatibility mode.
+            // Null token means the current foreground activity is not in compatibility mode.
             removeLayout(taskId);
         } else if (mActiveLayouts.contains(taskId)) {
             // UI already exists, update the UI layout.
             updateLayout(taskId, taskConfig, taskListener);
         } else {
-            // Create a new size compat UI.
+            // Create a new compat UI.
             createLayout(displayId, taskId, taskConfig, taskListener);
         }
     }
@@ -151,7 +152,7 @@
         mDisplayContextCache.remove(displayId);
         removeOnInsetsChangedListener(displayId);
 
-        // Remove all size compat UIs on the removed display.
+        // Remove all compat UIs on the removed display.
         final List<Integer> toRemoveTaskIds = new ArrayList<>();
         forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
         for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -194,7 +195,7 @@
             mDisplaysWithIme.remove(displayId);
         }
 
-        // Hide the size compat UIs when input method is showing.
+        // Hide the compat UIs when input method is showing.
         forAllLayoutsOnDisplay(displayId,
                 layout -> layout.updateVisibility(showOnDisplay(displayId)));
     }
@@ -202,7 +203,7 @@
     @VisibleForTesting
     void onKeyguardOccludedChanged(boolean occluded) {
         mKeyguardOccluded = occluded;
-        // Hide the size compat UIs when keyguard is occluded.
+        // Hide the compat UIs when keyguard is occluded.
         forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
     }
 
@@ -222,34 +223,34 @@
             return;
         }
 
-        final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
-                taskListener);
-        mActiveLayouts.put(taskId, layout);
-        layout.createSizeCompatButton(showOnDisplay(displayId));
+        final CompatUIWindowManager compatUIWindowManager =
+                createLayout(context, displayId, taskId, taskConfig, taskListener);
+        mActiveLayouts.put(taskId, compatUIWindowManager);
+        compatUIWindowManager.createLayout(showOnDisplay(displayId));
     }
 
     @VisibleForTesting
-    SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+    CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
             Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
-        final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context,
-                taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId),
-                mHasShownHint);
+        final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+                taskConfig, mSyncQueue, mCallback, taskId, taskListener,
+                mDisplayController.getDisplayLayout(displayId), mHasShownHint);
         // Only show hint for the first time.
         mHasShownHint = true;
-        return layout;
+        return compatUIWindowManager;
     }
 
     private void updateLayout(int taskId, Configuration taskConfig,
             ShellTaskOrganizer.TaskListener taskListener) {
-        final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+        final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
         if (layout == null) {
             return;
         }
-        layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+        layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
     }
 
     private void removeLayout(int taskId) {
-        final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+        final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
         if (layout != null) {
             layout.release();
             mActiveLayouts.remove(taskId);
@@ -275,19 +276,19 @@
         return context;
     }
 
-    private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
         forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
     }
 
-    private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
         forAllLayouts(layout -> true, callback);
     }
 
-    private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
-            Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
+            Consumer<CompatUIWindowManager> callback) {
         for (int i = 0; i < mActiveLayouts.size(); i++) {
             final int taskId = mActiveLayouts.keyAt(i);
-            final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+            final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
             if (layout != null && condition.test(layout)) {
                 callback.accept(layout);
             }
@@ -298,11 +299,11 @@
      * The interface for calls from outside the Shell, within the host process.
      */
     @ExternalThread
-    private class SizeCompatUIImpl implements SizeCompatUI {
+    private class CompatUIImpl implements CompatUI {
         @Override
         public void onKeyguardOccludedChanged(boolean occluded) {
             mMainExecutor.execute(() -> {
-                SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
+                CompatUIController.this.onKeyguardOccludedChanged(occluded);
             });
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
new file mode 100644
index 0000000..ea4f209
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -0,0 +1,89 @@
+/*
+ * 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.compatui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for compat UI controls.
+ */
+public class CompatUILayout extends LinearLayout {
+
+    private CompatUIWindowManager mWindowManager;
+
+    public CompatUILayout(Context context) {
+        this(context, null);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    void inject(CompatUIWindowManager windowManager) {
+        mWindowManager = windowManager;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        // Need to relayout after changes like hiding / showing a hint since they affect size.
+        // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+        mWindowManager.relayout();
+    }
+
+    void setSizeCompatHintVisibility(boolean show) {
+        final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+        int visibility = show ? View.VISIBLE : View.GONE;
+        if (sizeCompatHint.getVisibility() == visibility) {
+            return;
+        }
+        sizeCompatHint.setVisibility(visibility);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
+        restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked());
+        restartButton.setOnLongClickListener(view -> {
+            mWindowManager.onRestartButtonLongClicked();
+            return true;
+        });
+
+        final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+        ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
+                .setText(R.string.restart_button_description);
+        sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
new file mode 100644
index 0000000..997ad04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -0,0 +1,321 @@
+/*
+ * 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.compatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
+ * controls.
+ */
+class CompatUIWindowManager extends WindowlessWindowManager {
+
+    private static final String TAG = "CompatUIWindowManager";
+
+    private final SyncTransactionQueue mSyncQueue;
+    private final CompatUIController.CompatUICallback mCallback;
+    private final int mDisplayId;
+    private final int mTaskId;
+    private final Rect mStableBounds;
+
+    private Context mContext;
+    private Configuration mTaskConfig;
+    private ShellTaskOrganizer.TaskListener mTaskListener;
+    private DisplayLayout mDisplayLayout;
+
+    @VisibleForTesting
+    boolean mShouldShowHint;
+
+    @Nullable
+    @VisibleForTesting
+    CompatUILayout mCompatUILayout;
+
+    @Nullable
+    private SurfaceControlViewHost mViewHost;
+    @Nullable
+    private SurfaceControl mLeash;
+
+    CompatUIWindowManager(Context context, Configuration taskConfig,
+            SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
+            int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
+            boolean hasShownHint) {
+        super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+        mContext = context;
+        mSyncQueue = syncQueue;
+        mCallback = callback;
+        mTaskConfig = taskConfig;
+        mDisplayId = mContext.getDisplayId();
+        mTaskId = taskId;
+        mTaskListener = taskListener;
+        mDisplayLayout = displayLayout;
+        mShouldShowHint = !hasShownHint;
+        mStableBounds = new Rect();
+        mDisplayLayout.getStableBounds(mStableBounds);
+    }
+
+    @Override
+    public void setConfiguration(Configuration configuration) {
+        super.setConfiguration(configuration);
+        mContext = mContext.createConfigurationContext(configuration);
+    }
+
+    @Override
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+        // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+                .setContainerLayer()
+                .setName("CompatUILeash")
+                .setHidden(false)
+                .setCallsite("CompatUIWindowManager#attachToParentSurface");
+        attachToParentSurface(builder);
+        mLeash = builder.build();
+        b.setParent(mLeash);
+    }
+
+    /** Creates the layout for compat controls. */
+    void createLayout(boolean show) {
+        if (!show || mCompatUILayout != null) {
+            // Wait until compat controls should be visible.
+            return;
+        }
+
+        initCompatUi();
+        updateSurfacePosition();
+
+        mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+    }
+
+    /** Called when compat info changed. */
+    void updateCompatInfo(Configuration taskConfig,
+            ShellTaskOrganizer.TaskListener taskListener, boolean show) {
+        final Configuration prevTaskConfig = mTaskConfig;
+        final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+        mTaskConfig = taskConfig;
+        mTaskListener = taskListener;
+
+        // Update configuration.
+        mContext = mContext.createConfigurationContext(taskConfig);
+        setConfiguration(taskConfig);
+
+        if (mCompatUILayout == null || prevTaskListener != taskListener) {
+            // TaskListener changed, recreate the layout for new surface parent.
+            release();
+            createLayout(show);
+            return;
+        }
+
+        if (!taskConfig.windowConfiguration.getBounds()
+                .equals(prevTaskConfig.windowConfiguration.getBounds())) {
+            // Reposition the UI surfaces.
+            updateSurfacePosition();
+        }
+
+        if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
+            // Update layout for RTL.
+            mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
+            updateSurfacePosition();
+        }
+    }
+
+    /** Called when the visibility of the UI should change. */
+    void updateVisibility(boolean show) {
+        if (mCompatUILayout == null) {
+            // Layout may not have been created because it was hidden previously.
+            createLayout(show);
+            return;
+        }
+
+        // Hide compat UIs when IME is showing.
+        final int newVisibility = show ? View.VISIBLE : View.GONE;
+        if (mCompatUILayout.getVisibility() != newVisibility) {
+            mCompatUILayout.setVisibility(newVisibility);
+        }
+    }
+
+    /** Called when display layout changed. */
+    void updateDisplayLayout(DisplayLayout displayLayout) {
+        final Rect prevStableBounds = mStableBounds;
+        final Rect curStableBounds = new Rect();
+        displayLayout.getStableBounds(curStableBounds);
+        mDisplayLayout = displayLayout;
+        if (!prevStableBounds.equals(curStableBounds)) {
+            // Stable bounds changed, update UI surface positions.
+            updateSurfacePosition();
+            mStableBounds.set(curStableBounds);
+        }
+    }
+
+    /** Called when it is ready to be placed compat UI surface. */
+    void attachToParentSurface(SurfaceControl.Builder b) {
+        mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+    }
+
+    /** Called when the restart button is clicked. */
+    void onRestartButtonClicked() {
+        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+    }
+
+    /** Called when the restart button is long clicked. */
+    void onRestartButtonLongClicked() {
+        if (mCompatUILayout == null) {
+            return;
+        }
+        mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+    }
+
+    int getDisplayId() {
+        return mDisplayId;
+    }
+
+    int getTaskId() {
+        return mTaskId;
+    }
+
+    /** Releases the surface control and tears down the view hierarchy. */
+    void release() {
+        mCompatUILayout = null;
+
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        if (mLeash != null) {
+            final SurfaceControl leash = mLeash;
+            mSyncQueue.runInSync(t -> t.remove(leash));
+            mLeash = null;
+        }
+    }
+
+    void relayout() {
+        mViewHost.relayout(getWindowLayoutParams());
+        updateSurfacePosition();
+    }
+
+    @VisibleForTesting
+    void updateSurfacePosition() {
+        if (mCompatUILayout == null || mLeash == null) {
+            return;
+        }
+
+        // Use stable bounds to prevent controls from overlapping with system bars.
+        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+        final Rect stableBounds = new Rect();
+        mDisplayLayout.getStableBounds(stableBounds);
+        stableBounds.intersect(taskBounds);
+
+        // Position of the button in the container coordinate.
+        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                ? stableBounds.left - taskBounds.left
+                : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+        final int positionY = stableBounds.bottom - taskBounds.top
+                - mCompatUILayout.getMeasuredHeight();
+
+        updateSurfacePosition(positionX, positionY);
+    }
+
+    private int getLayoutDirection() {
+        return mContext.getResources().getConfiguration().getLayoutDirection();
+    }
+
+    private void updateSurfacePosition(int positionX, int positionY) {
+        mSyncQueue.runInSync(t -> {
+            if (mLeash == null || !mLeash.isValid()) {
+                Log.w(TAG, "The leash has been released.");
+                return;
+            }
+            t.setPosition(mLeash, positionX, positionY);
+            // The compat UI should be the topmost child of the Task in case there can be more
+            // than one children.
+            t.setLayer(mLeash, Integer.MAX_VALUE);
+        });
+    }
+
+    /** Inflates {@link CompatUILayout} on to the root surface. */
+    private void initCompatUi() {
+        if (mViewHost != null) {
+            throw new IllegalStateException(
+                    "A UI has already been created with this window manager.");
+        }
+
+        // Construction extracted into the separate methods to allow injection for tests.
+        mViewHost = createSurfaceViewHost();
+        mCompatUILayout = inflateCompatUILayout();
+        mCompatUILayout.inject(this);
+
+        mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
+
+        mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+
+        // Only show by default for the first time.
+        mShouldShowHint = false;
+    }
+
+    @VisibleForTesting
+    CompatUILayout inflateCompatUILayout() {
+        return (CompatUILayout) LayoutInflater.from(mContext)
+                .inflate(R.layout.compat_ui_layout, null);
+    }
+
+    @VisibleForTesting
+    SurfaceControlViewHost createSurfaceViewHost() {
+        return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+    }
+
+    /** Gets the layout params. */
+    private WindowManager.LayoutParams getWindowLayoutParams() {
+        // Measure how big the hint is since its size depends on the text size.
+        mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+                // Cannot be wrap_content as this determines the actual window size
+                mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
+                TYPE_APPLICATION_OVERLAY,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.TRANSLUCENT);
+        winParams.token = new Binder();
+        winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
+        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+        return winParams;
+    }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 54ce6bb..6d158d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.TaskViewFactoryController;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.apppairs.AppPairsController;
@@ -54,6 +55,8 @@
 import com.android.wm.shell.common.annotations.ShellAnimationThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
 import com.android.wm.shell.draganddrop.DragAndDrop;
@@ -75,8 +78,6 @@
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
-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.startingsurface.StartingSurface;
@@ -173,25 +174,25 @@
     @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
             Context context,
-            SizeCompatUIController sizeCompatUI,
+            CompatUIController compatUI,
             Optional<RecentTasksController> recentTasksOptional
     ) {
-        return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
+        return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
     }
 
     @WMSingleton
     @Provides
-    static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
-        return sizeCompatUIController.asSizeCompatUI();
+    static CompatUI provideCompatUI(CompatUIController compatUIController) {
+        return compatUIController.asCompatUI();
     }
 
     @WMSingleton
     @Provides
-    static SizeCompatUIController provideSizeCompatUIController(Context context,
+    static CompatUIController provideCompatUIController(Context context,
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new SizeCompatUIController(context, displayController, displayInsetsController,
+        return new CompatUIController(context, displayController, displayInsetsController,
                 imeController, syncQueue, mainExecutor);
     }
 
@@ -463,6 +464,12 @@
                 animExecutor);
     }
 
+    @WMSingleton
+    @Provides
+    static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) {
+        return new TaskViewTransitions(transitions);
+    }
+
     //
     // Display areas
     //
@@ -594,8 +601,10 @@
     static TaskViewFactoryController provideTaskViewFactoryController(
             ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor,
-            SyncTransactionQueue syncQueue) {
-        return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
+            SyncTransactionQueue syncQueue,
+            TaskViewTransitions taskViewTransitions) {
+        return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue,
+                taskViewTransitions);
     }
 
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f562fd9c..dff5635 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairsController;
 import com.android.wm.shell.bubbles.BubbleController;
@@ -108,11 +109,13 @@
             DisplayController displayController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
+            TaskViewTransitions taskViewTransitions,
             SyncTransactionQueue syncQueue) {
         return BubbleController.create(context, null /* synchronizer */,
                 floatingContentCoordinator, statusBarService, windowManager,
                 windowManagerShellWrapper, launcherApps, taskStackListener,
-                uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue);
+                uiEventLogger, organizer, displayController, mainExecutor, mainHandler,
+                taskViewTransitions, syncQueue);
     }
 
     //
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 854fc60e..f0b2716 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
@@ -778,8 +778,8 @@
     }
 
     @Override
-    public boolean supportSizeCompatUI() {
-        // PIP doesn't support size compat.
+    public boolean supportCompatUI() {
+        // PIP doesn't support compat.
         return false;
     }
 
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 eb512af..101a55d 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
@@ -191,6 +191,7 @@
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
+        setShellRootAccessibilityWindow();
     }
 
     private void detachPipMenuView() {
@@ -546,6 +547,10 @@
             mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
         }
         mMenuState = menuState;
+        setShellRootAccessibilityWindow();
+    }
+
+    private void setShellRootAccessibilityWindow() {
         switch (mMenuState) {
             case MENU_STATE_NONE:
                 mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
deleted file mode 100644
index ff6f913..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
+++ /dev/null
@@ -1,66 +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 com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
-public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
-
-    private SizeCompatUILayout mLayout;
-
-    public SizeCompatHintPopup(Context context) {
-        super(context);
-    }
-
-    public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    void inject(SizeCompatUILayout layout) {
-        mLayout = layout;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
-        hintPopup.setOnClickListener(this);
-    }
-
-    @Override
-    public void onClick(View v) {
-        mLayout.dismissHint();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
deleted file mode 100644
index d75fe517..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ /dev/null
@@ -1,76 +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 com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Button to restart the size compat activity. */
-public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
-        View.OnLongClickListener {
-
-    private SizeCompatUILayout mLayout;
-
-    public SizeCompatRestartButton(@NonNull Context context) {
-        super(context);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    void inject(SizeCompatUILayout layout) {
-        mLayout = layout;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
-        restartButton.setOnClickListener(this);
-        restartButton.setOnLongClickListener(this);
-    }
-
-    @Override
-    public void onClick(View v) {
-        mLayout.onRestartButtonClicked();
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        mLayout.onRestartButtonLongClicked();
-        return true;
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
deleted file mode 100644
index c35b89a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ /dev/null
@@ -1,344 +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 com.android.wm.shell.sizecompatui;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or UI position changes.
- */
-class SizeCompatUILayout {
-    private static final String TAG = "SizeCompatUILayout";
-
-    final SyncTransactionQueue mSyncQueue;
-    private final SizeCompatUIController.SizeCompatUICallback mCallback;
-    private Context mContext;
-    private Configuration mTaskConfig;
-    private final int mDisplayId;
-    private final int mTaskId;
-    private ShellTaskOrganizer.TaskListener mTaskListener;
-    private DisplayLayout mDisplayLayout;
-    private final Rect mStableBounds;
-    private final int mButtonWidth;
-    private final int mButtonHeight;
-    private final int mPopupOffsetX;
-    private final int mPopupOffsetY;
-
-    @VisibleForTesting
-    final SizeCompatUIWindowManager mButtonWindowManager;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatUIWindowManager mHintWindowManager;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatRestartButton mButton;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatHintPopup mHint;
-    @VisibleForTesting
-    boolean mShouldShowHint;
-
-    SizeCompatUILayout(SyncTransactionQueue syncQueue,
-            SizeCompatUIController.SizeCompatUICallback callback, Context context,
-            Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener,
-            DisplayLayout displayLayout, boolean hasShownHint) {
-        mSyncQueue = syncQueue;
-        mCallback = callback;
-        mContext = context.createConfigurationContext(taskConfig);
-        mTaskConfig = taskConfig;
-        mDisplayId = mContext.getDisplayId();
-        mTaskId = taskId;
-        mTaskListener = taskListener;
-        mDisplayLayout = displayLayout;
-        mShouldShowHint = !hasShownHint;
-        mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
-
-        mStableBounds = new Rect();
-        mDisplayLayout.getStableBounds(mStableBounds);
-
-        final Resources resources = mContext.getResources();
-        mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
-        mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
-        mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
-                R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
-                R.dimen.size_compat_hint_point_width) / 2);
-        mPopupOffsetY = mButtonHeight;
-    }
-
-    /** Creates the activity restart button window. */
-    void createSizeCompatButton(boolean show) {
-        if (!show || mButton != null) {
-            // Wait until button should be visible.
-            return;
-        }
-        mButton = mButtonWindowManager.createSizeCompatButton();
-        updateButtonSurfacePosition();
-
-        if (mShouldShowHint) {
-            // Only show by default for the first time.
-            mShouldShowHint = false;
-            createSizeCompatHint();
-        }
-
-        mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
-    }
-
-    /** Creates the restart button hint window. */
-    private void createSizeCompatHint() {
-        if (mHint != null) {
-            // Hint already shown.
-            return;
-        }
-        mHintWindowManager = createHintWindowManager();
-        mHint = mHintWindowManager.createSizeCompatHint();
-        updateHintSurfacePosition();
-    }
-
-    @VisibleForTesting
-    SizeCompatUIWindowManager createHintWindowManager() {
-        return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
-    }
-
-    /** Dismisses the hint window. */
-    void dismissHint() {
-        mHint = null;
-        if (mHintWindowManager != null) {
-            mHintWindowManager.release();
-            mHintWindowManager = null;
-        }
-    }
-
-    /** Releases the UI windows. */
-    void release() {
-        dismissHint();
-        mButton = null;
-        mButtonWindowManager.release();
-    }
-
-    /** Called when size compat info changed. */
-    void updateSizeCompatInfo(Configuration taskConfig,
-            ShellTaskOrganizer.TaskListener taskListener, boolean show) {
-        final Configuration prevTaskConfig = mTaskConfig;
-        final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
-        mTaskConfig = taskConfig;
-        mTaskListener = taskListener;
-
-        // Update configuration.
-        mContext = mContext.createConfigurationContext(taskConfig);
-        mButtonWindowManager.setConfiguration(taskConfig);
-        if (mHintWindowManager != null) {
-            mHintWindowManager.setConfiguration(taskConfig);
-        }
-
-        if (mButton == null || prevTaskListener != taskListener) {
-            // TaskListener changed, recreate the button for new surface parent.
-            release();
-            createSizeCompatButton(show);
-            return;
-        }
-
-        if (!taskConfig.windowConfiguration.getBounds()
-                .equals(prevTaskConfig.windowConfiguration.getBounds())) {
-            // Reposition the UI surfaces.
-            updateAllSurfacePositions();
-        }
-
-        if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
-            // Update layout for RTL.
-            mButton.setLayoutDirection(taskConfig.getLayoutDirection());
-            updateButtonSurfacePosition();
-            if (mHint != null) {
-                mHint.setLayoutDirection(taskConfig.getLayoutDirection());
-                updateHintSurfacePosition();
-            }
-        }
-    }
-
-    /** Called when display layout changed. */
-    void updateDisplayLayout(DisplayLayout displayLayout) {
-        final Rect prevStableBounds = mStableBounds;
-        final Rect curStableBounds = new Rect();
-        displayLayout.getStableBounds(curStableBounds);
-        mDisplayLayout = displayLayout;
-        if (!prevStableBounds.equals(curStableBounds)) {
-            // Stable bounds changed, update UI surface positions.
-            updateAllSurfacePositions();
-            mStableBounds.set(curStableBounds);
-        }
-    }
-
-    /** Called when the visibility of the UI should change. */
-    void updateVisibility(boolean show) {
-        if (mButton == null) {
-            // Button may not have been created because it was hidden previously.
-            createSizeCompatButton(show);
-            return;
-        }
-
-        // Hide size compat UIs when IME is showing.
-        final int newVisibility = show ? View.VISIBLE : View.GONE;
-        if (mButton.getVisibility() != newVisibility) {
-            mButton.setVisibility(newVisibility);
-        }
-        if (mHint != null && mHint.getVisibility() != newVisibility) {
-            mHint.setVisibility(newVisibility);
-        }
-    }
-
-    /** Gets the layout params for restart button. */
-    WindowManager.LayoutParams getButtonWindowLayoutParams() {
-        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
-                // Cannot be wrap_content as this determines the actual window size
-                mButtonWidth, mButtonHeight,
-                TYPE_APPLICATION_OVERLAY,
-                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
-                PixelFormat.TRANSLUCENT);
-        winParams.token = new Binder();
-        winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
-        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
-        return winParams;
-    }
-
-    /** Gets the layout params for hint popup. */
-    WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
-        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
-                // Cannot be wrap_content as this determines the actual window size
-                hint.getMeasuredWidth(), hint.getMeasuredHeight(),
-                TYPE_APPLICATION_OVERLAY,
-                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
-                PixelFormat.TRANSLUCENT);
-        winParams.token = new Binder();
-        winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
-        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
-        winParams.windowAnimations = android.R.style.Animation_InputMethod;
-        return winParams;
-    }
-
-    /** Called when it is ready to be placed size compat UI surface. */
-    void attachToParentSurface(SurfaceControl.Builder b) {
-        mTaskListener.attachChildSurfaceToTask(mTaskId, b);
-    }
-
-    /** Called when the restart button is clicked. */
-    void onRestartButtonClicked() {
-        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
-    }
-
-    /** Called when the restart button is long clicked. */
-    void onRestartButtonLongClicked() {
-        createSizeCompatHint();
-    }
-
-    private void updateAllSurfacePositions() {
-        updateButtonSurfacePosition();
-        updateHintSurfacePosition();
-    }
-
-    @VisibleForTesting
-    void updateButtonSurfacePosition() {
-        if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
-            return;
-        }
-        final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
-
-        // Use stable bounds to prevent the button from overlapping with system bars.
-        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
-        final Rect stableBounds = new Rect();
-        mDisplayLayout.getStableBounds(stableBounds);
-        stableBounds.intersect(taskBounds);
-
-        // Position of the button in the container coordinate.
-        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
-                ? stableBounds.left - taskBounds.left
-                : stableBounds.right - taskBounds.left - mButtonWidth;
-        final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
-
-        updateSurfacePosition(leash, positionX, positionY);
-    }
-
-    @VisibleForTesting
-    void updateHintSurfacePosition() {
-        if (mHint == null || mHintWindowManager == null
-                || mHintWindowManager.getSurfaceControl() == null) {
-            return;
-        }
-        final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
-
-        // Use stable bounds to prevent the hint from overlapping with system bars.
-        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
-        final Rect stableBounds = new Rect();
-        mDisplayLayout.getStableBounds(stableBounds);
-        stableBounds.intersect(taskBounds);
-
-        // Position of the hint in the container coordinate.
-        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
-                ? stableBounds.left - taskBounds.left + mPopupOffsetX
-                : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
-        final int positionY =
-                stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
-
-        updateSurfacePosition(leash, positionX, positionY);
-    }
-
-    private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
-        mSyncQueue.runInSync(t -> {
-            if (!leash.isValid()) {
-                Log.w(TAG, "The leash has been released.");
-                return;
-            }
-            t.setPosition(leash, positionX, positionY);
-            // The size compat UI should be the topmost child of the Task in case there can be more
-            // than one children.
-            t.setLayer(leash, Integer.MAX_VALUE);
-        });
-    }
-
-    int getDisplayId() {
-        return mDisplayId;
-    }
-
-    int getTaskId() {
-        return mTaskId;
-    }
-
-    private int getLayoutDirection() {
-        return mContext.getResources().getConfiguration().getLayoutDirection();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
deleted file mode 100644
index 82f69c3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ /dev/null
@@ -1,127 +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 com.android.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.IWindow;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowlessWindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
- * {@link SizeCompatHintPopup}.
- */
-class SizeCompatUIWindowManager extends WindowlessWindowManager {
-
-    private Context mContext;
-    private final SizeCompatUILayout mLayout;
-
-    @Nullable
-    private SurfaceControlViewHost mViewHost;
-    @Nullable
-    private SurfaceControl mLeash;
-
-    SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
-        super(config, null /* rootSurface */, null /* hostInputToken */);
-        mContext = context;
-        mLayout = layout;
-    }
-
-    @Override
-    public void setConfiguration(Configuration configuration) {
-        super.setConfiguration(configuration);
-        mContext = mContext.createConfigurationContext(configuration);
-    }
-
-    @Override
-    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
-        // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
-        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
-                .setContainerLayer()
-                .setName("SizeCompatUILeash")
-                .setHidden(false)
-                .setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
-        mLayout.attachToParentSurface(builder);
-        mLeash = builder.build();
-        b.setParent(mLeash);
-    }
-
-    /** Inflates {@link SizeCompatRestartButton} on to the root surface. */
-    SizeCompatRestartButton createSizeCompatButton() {
-        if (mViewHost != null) {
-            throw new IllegalStateException(
-                    "A UI has already been created with this window manager.");
-        }
-
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
-        final SizeCompatRestartButton button = (SizeCompatRestartButton)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
-        button.inject(mLayout);
-        mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
-        return button;
-    }
-
-    /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
-    SizeCompatHintPopup createSizeCompatHint() {
-        if (mViewHost != null) {
-            throw new IllegalStateException(
-                    "A UI has already been created with this window manager.");
-        }
-
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
-        final SizeCompatHintPopup hint = (SizeCompatHintPopup)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
-        // Measure how big the hint is.
-        hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-        hint.inject(mLayout);
-        mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
-        return hint;
-    }
-
-    /** Releases the surface control and tears down the view hierarchy. */
-    void release() {
-        if (mViewHost != null) {
-            mViewHost.release();
-            mViewHost = null;
-        }
-
-        if (mLeash != null) {
-            final SurfaceControl leash = mLeash;
-            mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
-            mLeash = null;
-        }
-    }
-
-    /**
-     * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
-     * if not feasible.
-     */
-    @Nullable
-    SurfaceControl getSurfaceControl() {
-        return mLeash;
-    }
-}
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 05552aa..cf4e56e 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
@@ -102,6 +102,7 @@
     static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
     static final int EXIT_REASON_SCREEN_LOCKED = 7;
     static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+    static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
     @IntDef(value = {
             EXIT_REASON_UNKNOWN,
             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -112,6 +113,7 @@
             EXIT_REASON_ROOT_TASK_VANISHED,
             EXIT_REASON_SCREEN_LOCKED,
             EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
+            EXIT_REASON_CHILD_TASK_ENTER_PIP,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ExitReason{}
@@ -406,6 +408,8 @@
                 return "APP_FINISHED";
             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
                 return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
+                return "CHILD_TASK_ENTER_PIP";
             default:
                 return "unknown reason, reason int = " + exitReason;
         }
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 d1feee4..ed5de0d 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
@@ -35,6 +35,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -629,8 +630,12 @@
         });
         mShouldUpdateRecents = false;
 
-        mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
-        mMainStage.deactivate(wct, childrenToTop == mMainStage);
+        // When the exit split-screen is caused by one of the task enters auto pip,
+        // we want the tasks to be put to bottom instead of top, otherwise it will end up
+        // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
+        final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
+        mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage);
+        mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage);
         mTaskOrganizer.applyTransaction(wct);
         mSyncQueue.runInSync(t -> t
                 .setWindowCrop(mMainStage.mRootLeash, null)
@@ -660,6 +665,8 @@
             case EXIT_REASON_DRAG_DIVIDER:
             // Either of the split apps have finished
             case EXIT_REASON_APP_FINISHED:
+            // One of the children enters PiP
+            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
                 return true;
             default:
                 return false;
@@ -749,6 +756,11 @@
         }
     }
 
+    private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
+        exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
+                EXIT_REASON_CHILD_TASK_ENTER_PIP);
+    }
+
     private void updateRecentTasksSplitPair() {
         if (!mShouldUpdateRecents) {
             return;
@@ -1443,6 +1455,11 @@
         }
 
         @Override
+        public void onChildTaskEnterPip(int taskId) {
+            StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
+        }
+
+        @Override
         public void onRootTaskVanished() {
             reset();
             StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index cd10b9f..2c853c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -73,6 +74,8 @@
 
         void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
 
+        void onChildTaskEnterPip(int taskId);
+
         void onRootTaskVanished();
 
         void onNoLongerSupportMultiWindow();
@@ -256,6 +259,9 @@
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
             mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
+            if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+                mCallbacks.onChildTaskEnterPip(taskId);
+            }
             if (ENABLE_SHELL_TRANSITIONS) {
                 // Status is managed/synchronized by the transition lifecycle.
                 return;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 1fcbf14..a3b98a8f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -56,7 +56,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
+import com.android.wm.shell.compatui.CompatUIController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -82,7 +82,7 @@
     @Mock
     private Context mContext;
     @Mock
-    private SizeCompatUIController mSizeCompatUI;
+    private CompatUIController mCompatUI;
 
     ShellTaskOrganizer mOrganizer;
     private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -132,7 +132,7 @@
                     .when(mTaskOrganizerController).registerTaskOrganizer(any());
         } catch (RemoteException e) {}
         mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
-                mSizeCompatUI, Optional.empty()));
+                mCompatUI, Optional.empty()));
     }
 
     @Test
@@ -334,34 +334,34 @@
         mOrganizer.onTaskAppeared(taskInfo1, null);
 
         // sizeCompatActivity is null if top activity is not in size compat.
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
 
         // sizeCompatActivity is non-null if top activity is in size compat.
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         final RunningTaskInfo taskInfo2 =
                 createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
         taskInfo2.displayId = taskInfo1.displayId;
         taskInfo2.topActivityInSizeCompat = true;
         taskInfo2.isVisible = true;
         mOrganizer.onTaskInfoChanged(taskInfo2);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 taskInfo1.configuration, taskListener);
 
         // Not show size compat UI if task is not visible.
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         final RunningTaskInfo taskInfo3 =
                 createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
         taskInfo3.displayId = taskInfo1.displayId;
         taskInfo3.topActivityInSizeCompat = true;
         taskInfo3.isVisible = false;
         mOrganizer.onTaskInfoChanged(taskInfo3);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
 
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         mOrganizer.onTaskVanished(taskInfo1);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 1cbad15..03df92f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -21,6 +21,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,12 +45,14 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
 import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
+import com.android.wm.shell.transition.Transitions;
 
 import org.junit.After;
 import org.junit.Before;
@@ -75,6 +79,8 @@
     HandlerExecutor mExecutor;
     @Mock
     SyncTransactionQueue mSyncQueue;
+    @Mock
+    TaskViewTransitions mTaskViewTransitions;
 
     SurfaceSession mSession;
     SurfaceControl mLeash;
@@ -110,7 +116,7 @@
             return null;
         }).when(mSyncQueue).runInSync(any());
 
-        mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+        mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
         mTaskView.setListener(mExecutor, mViewListener);
     }
 
@@ -123,7 +129,7 @@
 
     @Test
     public void testSetPendingListener_throwsException() {
-        TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+        TaskView taskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
         taskView.setListener(mExecutor, mViewListener);
         try {
             taskView.setListener(mExecutor, mViewListener);
@@ -144,7 +150,8 @@
     }
 
     @Test
-    public void testOnTaskAppeared_noSurface() {
+    public void testOnTaskAppeared_noSurface_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
 
         verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
@@ -154,7 +161,8 @@
     }
 
     @Test
-    public void testOnTaskAppeared_withSurface() {
+    public void testOnTaskAppeared_withSurface_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
 
@@ -163,7 +171,8 @@
     }
 
     @Test
-    public void testSurfaceCreated_noTask() {
+    public void testSurfaceCreated_noTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
 
         verify(mViewListener).onInitialized();
@@ -172,7 +181,8 @@
     }
 
     @Test
-    public void testSurfaceCreated_withTask() {
+    public void testSurfaceCreated_withTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
 
@@ -181,7 +191,8 @@
     }
 
     @Test
-    public void testSurfaceDestroyed_noTask() {
+    public void testSurfaceDestroyed_noTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         SurfaceHolder sh = mock(SurfaceHolder.class);
         mTaskView.surfaceCreated(sh);
         mTaskView.surfaceDestroyed(sh);
@@ -190,7 +201,8 @@
     }
 
     @Test
-    public void testSurfaceDestroyed_withTask() {
+    public void testSurfaceDestroyed_withTask_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         SurfaceHolder sh = mock(SurfaceHolder.class);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         mTaskView.surfaceCreated(sh);
@@ -201,7 +213,8 @@
     }
 
     @Test
-    public void testOnReleased() {
+    public void testOnReleased_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
         mTaskView.release();
@@ -211,7 +224,8 @@
     }
 
     @Test
-    public void testOnTaskVanished() {
+    public void testOnTaskVanished_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         mTaskView.surfaceCreated(mock(SurfaceHolder.class));
         mTaskView.onTaskVanished(mTaskInfo);
@@ -220,7 +234,8 @@
     }
 
     @Test
-    public void testOnBackPressedOnTaskRoot() {
+    public void testOnBackPressedOnTaskRoot_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
 
@@ -228,17 +243,158 @@
     }
 
     @Test
-    public void testSetOnBackPressedOnTaskRoot() {
+    public void testSetOnBackPressedOnTaskRoot_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
     }
 
     @Test
-    public void testUnsetOnBackPressedOnTaskRoot() {
+    public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
+        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
         mTaskView.onTaskAppeared(mTaskInfo, mLeash);
         verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
 
         mTaskView.onTaskVanished(mTaskInfo);
         verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
     }
+
+    @Test
+    public void testOnNewTask_noSurface() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onInitialized();
+        // If there's no surface the task should be made invisible
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testSurfaceCreated_noTask() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        verify(mTaskViewTransitions, never()).setTaskViewVisible(any(), anyBoolean());
+
+        verify(mViewListener).onInitialized();
+        // No task, no visibility change
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testOnNewTask_withSurface() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceCreated_withTask() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        verify(mViewListener).onInitialized();
+        verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(true));
+
+        mTaskView.prepareOpenAnimation(false /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+    }
+
+    @Test
+    public void testSurfaceDestroyed_noTask() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskView.surfaceCreated(sh);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceDestroyed_withTask() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        mTaskView.surfaceCreated(sh);
+        reset(mViewListener);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(false));
+
+        mTaskView.prepareHideAnimation(new SurfaceControl.Transaction());
+
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testOnReleased() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.release();
+
+        verify(mOrganizer).removeListener(eq(mTaskView));
+        verify(mViewListener).onReleased();
+        verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
+    }
+
+    @Test
+    public void testOnTaskVanished() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.prepareCloseAnimation();
+
+        verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+    }
+
+    @Test
+    public void testOnBackPressedOnTaskRoot() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+        verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+    }
+
+    @Test
+    public void testSetOnBackPressedOnTaskRoot() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+    }
+
+    @Test
+    public void testUnsetOnBackPressedOnTaskRoot() {
+        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+                new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+
+        mTaskView.prepareCloseAnimation();
+        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
similarity index 83%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 877b192..f622edb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 
@@ -56,18 +56,18 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * Tests for {@link SizeCompatUIController}.
+ * Tests for {@link CompatUIController}.
  *
  * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatUIControllerTest
+ *  atest WMShellUnitTests:CompatUIControllerTest
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class SizeCompatUIControllerTest extends ShellTestCase {
+public class CompatUIControllerTest extends ShellTestCase {
     private static final int DISPLAY_ID = 0;
     private static final int TASK_ID = 12;
 
-    private SizeCompatUIController mController;
+    private CompatUIController mController;
     private @Mock DisplayController mMockDisplayController;
     private @Mock DisplayInsetsController mMockDisplayInsetsController;
     private @Mock DisplayLayout mMockDisplayLayout;
@@ -75,7 +75,7 @@
     private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
     private @Mock SyncTransactionQueue mMockSyncQueue;
     private @Mock ShellExecutor mMockExecutor;
-    private @Mock SizeCompatUILayout mMockLayout;
+    private @Mock CompatUIWindowManager mMockLayout;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -87,10 +87,10 @@
         doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
         doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
         doReturn(TASK_ID).when(mMockLayout).getTaskId();
-        mController = new SizeCompatUIController(mContext, mMockDisplayController,
+        mController = new CompatUIController(mContext, mMockDisplayController,
                 mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
             @Override
-            SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+            CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
                     Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
                 return mMockLayout;
             }
@@ -105,24 +105,24 @@
     }
 
     @Test
-    public void testOnSizeCompatInfoChanged() {
+    public void testOnCompatInfoChanged() {
         final Configuration taskConfig = new Configuration();
 
         // Verify that the restart button is added with non-null size compat info.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
                 eq(mMockTaskListener));
 
         // Verify that the restart button is updated with non-null new size compat info.
         final Configuration newTaskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 true /* show */);
 
         // Verify that the restart button is removed with null size compat info.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
 
         verify(mMockLayout).release();
     }
@@ -140,7 +140,7 @@
     public void testOnDisplayRemoved() {
         mController.onDisplayAdded(DISPLAY_ID);
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
 
         mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -158,7 +158,7 @@
     @Test
     public void testOnDisplayConfigurationChanged() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
 
         final Configuration newTaskConfig = new Configuration();
@@ -175,7 +175,7 @@
     public void testInsetsChanged() {
         mController.onDisplayAdded(DISPLAY_ID);
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
         InsetsState insetsState = new InsetsState();
         InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
@@ -197,7 +197,7 @@
     @Test
     public void testChangeButtonVisibilityOnImeShowHide() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         // Verify that the restart button is hidden after IME is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,9 +205,9 @@
         verify(mMockLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 false /* show */);
 
         // Verify button is shown after IME is hidden.
@@ -219,7 +219,7 @@
     @Test
     public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         // Verify that the restart button is hidden after keyguard becomes occluded.
         mController.onKeyguardOccludedChanged(true);
@@ -227,9 +227,9 @@
         verify(mMockLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is occluded.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 false /* show */);
 
         // Verify button is shown after keyguard becomes not occluded.
@@ -241,7 +241,7 @@
     @Test
     public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
         mController.onKeyguardOccludedChanged(true);
@@ -264,7 +264,7 @@
     @Test
     public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
         mController.onKeyguardOccludedChanged(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
new file mode 100644
index 0000000..2c3987b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.compatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.SurfaceControlViewHost;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUILayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:CompatUILayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUILayoutTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private SurfaceControlViewHost mViewHost;
+
+    private CompatUIWindowManager mWindowManager;
+    private CompatUILayout mCompatUILayout;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+                mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+                false /* hasShownHint */);
+
+        mCompatUILayout = (CompatUILayout)
+                LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
+        mCompatUILayout.inject(mWindowManager);
+
+        spyOn(mWindowManager);
+        spyOn(mCompatUILayout);
+        doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+    }
+
+    @Test
+    public void testOnClickForRestartButton() {
+        final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+        button.performClick();
+
+        verify(mWindowManager).onRestartButtonClicked();
+        doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+    }
+
+    @Test
+    public void testOnLongClickForRestartButton() {
+        doNothing().when(mWindowManager).onRestartButtonLongClicked();
+
+        final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+        button.performLongClick();
+
+        verify(mWindowManager).onRestartButtonLongClicked();
+    }
+
+    @Test
+    public void testOnClickForSizeCompatHint() {
+        mWindowManager.createLayout(true /* show */);
+        final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
+        sizeCompatHint.performClick();
+
+        verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
new file mode 100644
index 0000000..d5dcf2e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.compatui;
+
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUIWindowManager}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:CompatUIWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUIWindowManagerTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private CompatUILayout mCompatUILayout;
+    @Mock private SurfaceControlViewHost mViewHost;
+    private Configuration mTaskConfig;
+
+    private CompatUIWindowManager mWindowManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTaskConfig = new Configuration();
+
+        mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+                mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+                false /* hasShownHint */);
+
+        spyOn(mWindowManager);
+        doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+        doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+    }
+
+    @Test
+    public void testCreateSizeCompatButton() {
+        // Not create layout if show is false.
+        mWindowManager.createLayout(false /* show */);
+
+        verify(mWindowManager, never()).inflateCompatUILayout();
+
+        // Not create hint popup.
+        mWindowManager.mShouldShowHint = false;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+        verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+        // Create hint popup.
+        mWindowManager.release();
+        mWindowManager.mShouldShowHint = true;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager, times(2)).inflateCompatUILayout();
+        assertNotNull(mCompatUILayout);
+        verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+        assertFalse(mWindowManager.mShouldShowHint);
+    }
+
+    @Test
+    public void testRelease() {
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+
+        mWindowManager.release();
+
+        verify(mViewHost).release();
+    }
+
+    @Test
+    public void testUpdateCompatInfo() {
+        mWindowManager.createLayout(true /* show */);
+
+        // No diff
+        clearInvocations(mWindowManager);
+        mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+
+        verify(mWindowManager, never()).updateSurfacePosition();
+        verify(mWindowManager, never()).release();
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+
+        // Change task listener, recreate button.
+        clearInvocations(mWindowManager);
+        final ShellTaskOrganizer.TaskListener newTaskListener = mock(
+                ShellTaskOrganizer.TaskListener.class);
+        mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+                true /* show */);
+
+        verify(mWindowManager).release();
+        verify(mWindowManager).createLayout(anyBoolean());
+
+        // Change task bounds, update position.
+        clearInvocations(mWindowManager);
+        final Configuration newTaskConfiguration = new Configuration();
+        newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+        mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
+                true /* show */);
+
+        verify(mWindowManager).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateDisplayLayout() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+
+        mWindowManager.updateDisplayLayout(displayLayout1);
+        verify(mWindowManager).updateSurfacePosition();
+
+        // No update if the display bounds is the same.
+        clearInvocations(mWindowManager);
+        final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+        mWindowManager.updateDisplayLayout(displayLayout2);
+        verify(mWindowManager, never()).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateDisplayLayoutInsets() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+        mWindowManager.updateDisplayLayout(displayLayout);
+        verify(mWindowManager).updateSurfacePosition();
+
+        // Update if the insets change on the existing display layout
+        clearInvocations(mWindowManager);
+        InsetsState insetsState = new InsetsState();
+        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+        insetsSource.setFrame(0, 0, 1000, 1000);
+        insetsState.addSource(insetsSource);
+        displayLayout.setInsets(mContext.getResources(), insetsState);
+        mWindowManager.updateDisplayLayout(displayLayout);
+        verify(mWindowManager).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateVisibility() {
+        // Create button if it is not created.
+        mWindowManager.mCompatUILayout = null;
+        mWindowManager.updateVisibility(true /* show */);
+
+        verify(mWindowManager).createLayout(true /* show */);
+
+        // Hide button.
+        clearInvocations(mWindowManager);
+        doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
+        mWindowManager.updateVisibility(false /* show */);
+
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+        verify(mCompatUILayout).setVisibility(View.GONE);
+
+        // Show button.
+        doReturn(View.GONE).when(mCompatUILayout).getVisibility();
+        mWindowManager.updateVisibility(true /* show */);
+
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+        verify(mCompatUILayout).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void testAttachToParentSurface() {
+        final SurfaceControl.Builder b = new SurfaceControl.Builder();
+        mWindowManager.attachToParentSurface(b);
+
+        verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
+    }
+
+    @Test
+    public void testOnRestartButtonClicked() {
+        mWindowManager.onRestartButtonClicked();
+
+        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+    }
+
+    @Test
+    public void testOnRestartButtonLongClicked_showHint() {
+       // Not create hint popup.
+        mWindowManager.mShouldShowHint = false;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+        verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+        mWindowManager.onRestartButtonLongClicked();
+
+        verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+    }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
deleted file mode 100644
index 3a14a33..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
+++ /dev/null
@@ -1,85 +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 com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatHintPopup}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatHintPopupTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatHintPopupTest extends ShellTestCase {
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-
-    private SizeCompatUILayout mLayout;
-    private SizeCompatHintPopup mHint;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        final int taskId = 1;
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), taskId, mTaskListener, mDisplayLayout,
-                false /* hasShownHint */);
-        mHint = (SizeCompatHintPopup)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
-        mHint.inject(mLayout);
-
-        spyOn(mLayout);
-    }
-
-    @Test
-    public void testOnClick() {
-        doNothing().when(mLayout).dismissHint();
-
-        final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
-        hintPopup.performClick();
-
-        verify(mLayout).dismissHint();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
deleted file mode 100644
index a20a5e9..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ /dev/null
@@ -1,95 +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 com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.ImageButton;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatRestartButton}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatRestartButtonTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatRestartButtonTest extends ShellTestCase {
-
-    private static final int TASK_ID = 1;
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-
-    private SizeCompatUILayout mLayout;
-    private SizeCompatRestartButton mButton;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
-                false /* hasShownHint */);
-        mButton = (SizeCompatRestartButton)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
-        mButton.inject(mLayout);
-
-        spyOn(mLayout);
-    }
-
-    @Test
-    public void testOnClick() {
-        final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
-        button.performClick();
-
-        verify(mLayout).onRestartButtonClicked();
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
-    }
-
-    @Test
-    public void testOnLongClick() {
-        doNothing().when(mLayout).onRestartButtonLongClicked();
-
-        final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
-        button.performLongClick();
-
-        verify(mLayout).onRestartButtonLongClicked();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
deleted file mode 100644
index eb9305b..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ /dev/null
@@ -1,284 +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 com.android.wm.shell.sizecompatui;
-
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.view.DisplayInfo;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatUILayout}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatUILayoutTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatUILayoutTest extends ShellTestCase {
-
-    private static final int TASK_ID = 1;
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-    @Mock private SizeCompatRestartButton mButton;
-    @Mock private SizeCompatHintPopup mHint;
-    private Configuration mTaskConfig;
-
-    private SizeCompatUILayout mLayout;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTaskConfig = new Configuration();
-
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
-                false /* hasShownHint */);
-
-        spyOn(mLayout);
-        spyOn(mLayout.mButtonWindowManager);
-        doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
-
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
-        spyOn(hintWindowManager);
-        doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
-        doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
-    }
-
-    @Test
-    public void testCreateSizeCompatButton() {
-        // Not create button if show is false.
-        mLayout.createSizeCompatButton(false /* show */);
-
-        verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
-        assertNull(mLayout.mButton);
-        assertNull(mLayout.mHintWindowManager);
-        assertNull(mLayout.mHint);
-
-        // Not create hint popup.
-        mLayout.mShouldShowHint = false;
-        mLayout.createSizeCompatButton(true /* show */);
-
-        verify(mLayout.mButtonWindowManager).createSizeCompatButton();
-        assertNotNull(mLayout.mButton);
-        assertNull(mLayout.mHintWindowManager);
-        assertNull(mLayout.mHint);
-
-        // Create hint popup.
-        mLayout.release();
-        mLayout.mShouldShowHint = true;
-        mLayout.createSizeCompatButton(true /* show */);
-
-        verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
-        assertNotNull(mLayout.mButton);
-        assertNotNull(mLayout.mHintWindowManager);
-        verify(mLayout.mHintWindowManager).createSizeCompatHint();
-        assertNotNull(mLayout.mHint);
-        assertFalse(mLayout.mShouldShowHint);
-    }
-
-    @Test
-    public void testRelease() {
-        mLayout.createSizeCompatButton(true /* show */);
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-
-        mLayout.release();
-
-        assertNull(mLayout.mButton);
-        assertNull(mLayout.mHint);
-        verify(hintWindowManager).release();
-        assertNull(mLayout.mHintWindowManager);
-        verify(mLayout.mButtonWindowManager).release();
-    }
-
-    @Test
-    public void testUpdateSizeCompatInfo() {
-        mLayout.createSizeCompatButton(true /* show */);
-
-        // No diff
-        clearInvocations(mLayout);
-        mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */);
-
-        verify(mLayout, never()).updateButtonSurfacePosition();
-        verify(mLayout, never()).release();
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-
-        // Change task listener, recreate button.
-        clearInvocations(mLayout);
-        final ShellTaskOrganizer.TaskListener newTaskListener = mock(
-                ShellTaskOrganizer.TaskListener.class);
-        mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener,
-                true /* show */);
-
-        verify(mLayout).release();
-        verify(mLayout).createSizeCompatButton(anyBoolean());
-
-        // Change task bounds, update position.
-        clearInvocations(mLayout);
-        final Configuration newTaskConfiguration = new Configuration();
-        newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
-        mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener,
-                true /* show */);
-
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateDisplayLayout() {
-        final DisplayInfo displayInfo = new DisplayInfo();
-        displayInfo.logicalWidth = 1000;
-        displayInfo.logicalHeight = 2000;
-        final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-
-        mLayout.updateDisplayLayout(displayLayout1);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-
-        // No update if the display bounds is the same.
-        clearInvocations(mLayout);
-        final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-        mLayout.updateDisplayLayout(displayLayout2);
-        verify(mLayout, never()).updateButtonSurfacePosition();
-        verify(mLayout, never()).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateDisplayLayoutInsets() {
-        final DisplayInfo displayInfo = new DisplayInfo();
-        displayInfo.logicalWidth = 1000;
-        displayInfo.logicalHeight = 2000;
-        final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
-
-        mLayout.updateDisplayLayout(displayLayout);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-
-        // Update if the insets change on the existing display layout
-        clearInvocations(mLayout);
-        InsetsState insetsState = new InsetsState();
-        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
-        insetsSource.setFrame(0, 0, 1000, 1000);
-        insetsState.addSource(insetsSource);
-        displayLayout.setInsets(mContext.getResources(), insetsState);
-        mLayout.updateDisplayLayout(displayLayout);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateVisibility() {
-        // Create button if it is not created.
-        mLayout.mButton = null;
-        mLayout.updateVisibility(true /* show */);
-
-        verify(mLayout).createSizeCompatButton(true /* show */);
-
-        // Hide button.
-        clearInvocations(mLayout);
-        doReturn(View.VISIBLE).when(mButton).getVisibility();
-        mLayout.updateVisibility(false /* show */);
-
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-        verify(mButton).setVisibility(View.GONE);
-
-        // Show button.
-        doReturn(View.GONE).when(mButton).getVisibility();
-        mLayout.updateVisibility(true /* show */);
-
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-        verify(mButton).setVisibility(View.VISIBLE);
-    }
-
-    @Test
-    public void testAttachToParentSurface() {
-        final SurfaceControl.Builder b = new SurfaceControl.Builder();
-        mLayout.attachToParentSurface(b);
-
-        verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
-    }
-
-    @Test
-    public void testOnRestartButtonClicked() {
-        mLayout.onRestartButtonClicked();
-
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
-    }
-
-    @Test
-    public void testOnRestartButtonLongClicked_showHint() {
-        mLayout.dismissHint();
-
-        assertNull(mLayout.mHint);
-
-        mLayout.onRestartButtonLongClicked();
-
-        assertNotNull(mLayout.mHint);
-    }
-
-    @Test
-    public void testDismissHint() {
-        mLayout.onRestartButtonLongClicked();
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-        assertNotNull(mLayout.mHint);
-        assertNotNull(hintWindowManager);
-
-        mLayout.dismissHint();
-
-        assertNull(mLayout.mHint);
-        assertNull(mLayout.mHintWindowManager);
-        verify(hintWindowManager).release();
-    }
-}
diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h
index 6d387f9..3df5c3c 100644
--- a/libs/hwui/ColorMode.h
+++ b/libs/hwui/ColorMode.h
@@ -29,6 +29,8 @@
     Hdr = 2,
     // HDR Rec2020 + 1010102
     Hdr10 = 3,
+    // Alpha 8
+    A8 = 4,
 };
 
 } // namespace android::uirenderer
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 9bca4df..744739a 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -91,6 +91,8 @@
         fboInfo.fFormat = GL_RGBA8;
     } else if (colorType == kRGBA_1010102_SkColorType) {
         fboInfo.fFormat = GL_RGB10_A2;
+    } else if (colorType == kAlpha_8_SkColorType) {
+        fboInfo.fFormat = GL_R8;
     } else {
         LOG_ALWAYS_FATAL("Unsupported color type.");
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 4e7471d..bc386fe 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -613,6 +613,10 @@
             mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
             mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
             break;
+        case ColorMode::A8:
+            mSurfaceColorType = SkColorType::kAlpha_8_SkColorType;
+            mSurfaceColorSpace = nullptr;
+            break;
     }
 }
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c7d7a17..2f8ddee 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -90,6 +90,7 @@
         , mEglConfig(nullptr)
         , mEglConfigF16(nullptr)
         , mEglConfig1010102(nullptr)
+        , mEglConfigA8(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
         , mCurrentSurface(EGL_NO_SURFACE)
@@ -246,6 +247,52 @@
     return config;
 }
 
+EGLConfig EglManager::loadA8Config(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
+    EGLint eglSwapBehavior =
+            (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+                        EGL_OPENGL_ES2_BIT,
+                        EGL_RED_SIZE,
+                        8,
+                        EGL_GREEN_SIZE,
+                        0,
+                        EGL_BLUE_SIZE,
+                        0,
+                        EGL_ALPHA_SIZE,
+                        0,
+                        EGL_DEPTH_SIZE,
+                        0,
+                        EGL_STENCIL_SIZE,
+                        STENCIL_BUFFER_SIZE,
+                        EGL_SURFACE_TYPE,
+                        EGL_WINDOW_BIT | eglSwapBehavior,
+                        EGL_NONE};
+    EGLint numConfigs = 1;
+    if (!eglChooseConfig(display, attribs, nullptr, numConfigs, &numConfigs)) {
+        return EGL_NO_CONFIG_KHR;
+    }
+
+    std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+    if (!eglChooseConfig(display, attribs, configs.data(), numConfigs, &numConfigs)) {
+        return EGL_NO_CONFIG_KHR;
+    }
+
+    // The component sizes passed to eglChooseConfig are minimums, so configs
+    // contains entries that exceed them. Choose one that matches the sizes
+    // exactly.
+    for (EGLConfig config : configs) {
+        EGLint r{0}, g{0}, b{0}, a{0};
+        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+        eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+        if (8 == r && 0 == g && 0 == b && 0 == a) {
+            return config;
+        }
+    }
+    return EGL_NO_CONFIG_KHR;
+}
+
 void EglManager::initExtensions() {
     auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
 
@@ -307,6 +354,10 @@
         ALOGW("Failed to initialize 101010-2 format, error = %s",
               eglErrorString());
     }
+    mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+    if (mEglConfigA8 == EGL_NO_CONFIG_KHR) {
+        ALOGE("Failed to initialize A8 format, error = %s", eglErrorString());
+    }
 }
 
 void EglManager::createContext() {
@@ -345,10 +396,14 @@
                                                      sk_sp<SkColorSpace> colorSpace) {
     LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
-    if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) {
+    if (!EglExtensions.noConfigContext) {
+        // The caller shouldn't use A8 if we cannot switch modes.
+        LOG_ALWAYS_FATAL_IF(colorMode == ColorMode::A8,
+                            "Cannot use A8 without EGL_KHR_no_config_context!");
+
+        // Cannot switch modes without EGL_KHR_no_config_context.
         colorMode = ColorMode::Default;
     }
-
     // The color space we want to use depends on whether linear blending is turned
     // on and whether the app has requested wide color gamut rendering. When wide
     // color gamut rendering is off, the app simply renders in the display's native
@@ -374,42 +429,57 @@
     EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
 
     EGLConfig config = mEglConfig;
-    if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
-        if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+    if (colorMode == ColorMode::A8) {
+        // A8 doesn't use a color space
+        config = mEglConfigA8;
+
+        LOG_ALWAYS_FATAL_IF(!mEglConfigA8, "Requested ColorMode::A8, but EGL lacks support!");
+    } else {
+        if (!mHasWideColorGamutSupport) {
             colorMode = ColorMode::Default;
-        } else {
-            config = mEglConfigF16;
         }
-    }
-    if (EglExtensions.glColorSpace) {
-        attribs[0] = EGL_GL_COLORSPACE_KHR;
-        switch (colorMode) {
-            case ColorMode::Default:
-                attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
-                break;
-            case ColorMode::WideColorGamut: {
-                skcms_Matrix3x3 colorGamut;
-                LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
-                                    "Could not get gamut matrix from color space");
-                if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
-                    attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
-                } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
-                    attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
-                } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) {
-                    attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
-                } else {
-                    LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
-                }
-                break;
-            }
-            case ColorMode::Hdr:
+
+        if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+            if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+                colorMode = ColorMode::Default;
+            } else {
                 config = mEglConfigF16;
-                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
-                break;
-            case ColorMode::Hdr10:
-                config = mEglConfig1010102;
-                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
-                break;
+            }
+        }
+        if (EglExtensions.glColorSpace) {
+            attribs[0] = EGL_GL_COLORSPACE_KHR;
+            switch (colorMode) {
+                case ColorMode::Default:
+                    attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+                    break;
+                case ColorMode::WideColorGamut: {
+                    skcms_Matrix3x3 colorGamut;
+                    LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+                                        "Could not get gamut matrix from color space");
+                    if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
+                        attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+                    } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+                        attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+                    } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) ==
+                               0) {
+                        attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                    } else {
+                        LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+                    }
+                    break;
+                }
+                case ColorMode::Hdr:
+                    config = mEglConfigF16;
+                    attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                    break;
+                case ColorMode::Hdr10:
+                    config = mEglConfig1010102;
+                    attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                    break;
+                case ColorMode::A8:
+                    LOG_ALWAYS_FATAL("Unreachable: A8 doesn't use a color space");
+                    break;
+            }
         }
     }
 
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 69f3ed0..fc6b28d 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -89,6 +89,7 @@
     static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
     static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
     static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior);
+    static EGLConfig loadA8Config(EGLDisplay display, SwapBehavior swapBehavior);
 
     void initExtensions();
     void createPBufferSurface();
@@ -100,6 +101,7 @@
     EGLConfig mEglConfig;
     EGLConfig mEglConfigF16;
     EGLConfig mEglConfig1010102;
+    EGLConfig mEglConfigA8;
     EGLContext mEglContext;
     EGLSurface mPBufferSurface;
     EGLSurface mCurrentSurface;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9e8a1e1..a9ff2c6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -35,6 +35,9 @@
 #include "pipeline/skia/ShaderCache.h"
 #include "renderstate/RenderState.h"
 
+#undef LOG_TAG
+#define LOG_TAG "VulkanManager"
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 611a4d9..7dd3561 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -24,6 +24,9 @@
 #include "VulkanManager.h"
 #include "utils/Color.h"
 
+#undef LOG_TAG
+#define LOG_TAG "VulkanSurface"
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -197,8 +200,9 @@
     outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType);
     outWindowInfo->colorspace = colorSpace;
     outWindowInfo->dataspace = ColorSpaceToADataSpace(colorSpace.get(), colorType);
-    LOG_ALWAYS_FATAL_IF(outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN,
-                        "Unsupported colorspace");
+    LOG_ALWAYS_FATAL_IF(
+            outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN && colorType != kAlpha_8_SkColorType,
+            "Unsupported colorspace");
 
     VkFormat vkPixelFormat;
     switch (colorType) {
@@ -211,6 +215,9 @@
         case kRGBA_1010102_SkColorType:
             vkPixelFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
             break;
+        case kAlpha_8_SkColorType:
+            vkPixelFormat = VK_FORMAT_R8_UNORM;
+            break;
         default:
             LOG_ALWAYS_FATAL("Unsupported colorType: %d", (int)colorType);
     }
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9f3be16..2293ace 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -57,6 +57,10 @@
             colorType = kRGBA_F16_SkColorType;
             alphaType = kPremul_SkAlphaType;
             break;
+        case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+            colorType = kAlpha_8_SkColorType;
+            alphaType = kPremul_SkAlphaType;
+            break;
         default:
             ALOGV("Unsupported format: %d, return unknown by default", format);
             break;
@@ -90,6 +94,8 @@
             // Hardcoding the value from android::PixelFormat
             static constexpr uint64_t kRGBA4444 = 7;
             return kRGBA4444;
+        case kAlpha_8_SkColorType:
+              return AHARDWAREBUFFER_FORMAT_R8_UNORM;
         default:
             ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
             return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 7caac89..1448c49 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -110,7 +110,7 @@
             mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
         } else if (role == ROLE_INPUT) {
             AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
-            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type, address);
         } else {
             mNativeType = AudioSystem.DEVICE_NONE;
         }
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index a186566..211a50e 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -581,7 +581,16 @@
 
     /** @hide */
     public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
-        return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+        return convertDeviceTypeToInternalInputDevice(deviceType, "");
+    }
+    /** @hide */
+    public static int convertDeviceTypeToInternalInputDevice(int deviceType, String address) {
+        int internalType = EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+        if (internalType == AudioSystem.DEVICE_IN_BUILTIN_MIC
+                && "back".equals(address)) {
+            internalType = AudioSystem.DEVICE_IN_BACK_MIC;
+        }
+        return internalType;
     }
 
     private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
@@ -671,9 +680,6 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_USB_ACCESSORY, AudioSystem.DEVICE_OUT_USB_ACCESSORY);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_FM, AudioSystem.DEVICE_OUT_FM);
-        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
-        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
-        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_AUX_LINE, AudioSystem.DEVICE_OUT_AUX_LINE);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_OUT_IP);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d721291..0722417 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4003,8 +4003,7 @@
      * @hide
      * flag set on test API calls,
      * see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
-     * note that it isn't used in conjunction with other flags, it is passed as the single
-     * value for flags */
+     */
     public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
     /** @hide */
     public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
@@ -4187,7 +4186,9 @@
                     afr.getFocusGain(),
                     mICallBack,
                     mAudioFocusDispatcher,
-                    clientFakeId, "com.android.test.fakeclient", clientFakeUid, clientTargetSdk);
+                    clientFakeId, "com.android.test.fakeclient",
+                    afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
+                    clientFakeUid, clientTargetSdk);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index cc37c38..e8792b3 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1915,7 +1915,7 @@
             types[i] = devices.get(i).getInternalType();
             if (types[i] == AudioSystem.DEVICE_NONE) {
                 types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
-                        devices.get(i).getType());
+                        devices.get(i).getType(), devices.get(i).getAddress());
             }
             addresses[i] = devices.get(i).getAddress();
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index afcbc57..147e735 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -384,7 +384,7 @@
 
     int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb,
             in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
-            int uid, int sdk);
+            int flags, int uid, int sdk);
 
     int abandonAudioFocusForTest(in IAudioFocusDispatcher fd, in String clientId,
             in AudioAttributes aa, in String callingPackageName);
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 70bb960..28f238e 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -511,7 +511,12 @@
         int deviceType = AudioSystem.DEVICE_NONE;
         String deviceAddress = "";
         if (device != null) {
-            deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+            if (device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT) {
+                deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+            } else {
+                deviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+                        device.getType(), device.getAddress());
+            }
             deviceAddress = device.getAddress();
         }
 
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 253b4e3..00c4a97 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -185,7 +185,7 @@
     auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
     soundpool::Stream* stream = mStreamManager.findStream(streamID);
     if (stream != nullptr && stream->requestStop(streamID)) {
-        mStreamManager.moveToRestartQueue(stream);
+        mStreamManager.moveToRestartQueue(stream, streamID);
     }
 }
 
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 3e65df2..632dfb3 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
     <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <application
         android:label="@string/app_name"
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
new file mode 100644
index 0000000..3e82b28
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -0,0 +1,126 @@
+//
+// 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// NetworkStats related libraries.
+
+filegroup {
+    name: "framework-connectivity-netstats-internal-sources",
+    srcs: [
+        "src/android/app/usage/*.java",
+        "src/android/net/DataUsage*.*",
+        "src/android/net/INetworkStats*.*",
+        "src/android/net/NetworkIdentity*.java",
+        "src/android/net/NetworkStateSnapshot.*",
+        "src/android/net/NetworkStats*.*",
+        "src/android/net/NetworkTemplate.*",
+        "src/android/net/TrafficStats.java",
+        "src/android/net/UnderlyingNetworkInfo.*",
+        "src/android/net/netstats/**/*.*",
+    ],
+    path: "src",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+filegroup {
+    name: "framework-connectivity-netstats-aidl-export-sources",
+    srcs: [
+        "aidl-export/android/net/NetworkStats.aidl",
+        "aidl-export/android/net/NetworkTemplate.aidl",
+    ],
+    path: "aidl-export",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+filegroup {
+    name: "framework-connectivity-netstats-sources",
+    srcs: [
+        ":framework-connectivity-netstats-internal-sources",
+        ":framework-connectivity-netstats-aidl-export-sources",
+    ],
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+// Nsd related libraries.
+
+filegroup {
+    name: "framework-connectivity-nsd-internal-sources",
+    srcs: [
+        "src/android/net/nsd/*.aidl",
+        "src/android/net/nsd/*.java",
+    ],
+    path: "src",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+filegroup {
+    name: "framework-connectivity-nsd-aidl-export-sources",
+    srcs: [
+        "aidl-export/android/net/nsd/*.aidl",
+    ],
+    path: "aidl-export",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+filegroup {
+    name: "framework-connectivity-nsd-sources",
+    srcs: [
+        ":framework-connectivity-nsd-internal-sources",
+        ":framework-connectivity-nsd-aidl-export-sources",
+    ],
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+// Connectivity-T common libraries.
+
+filegroup {
+    name: "framework-connectivity-tiramisu-internal-sources",
+    srcs: [
+        "src/android/net/ConnectivityFrameworkInitializerTiramisu.java",
+    ],
+    path: "src",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+filegroup {
+    name: "framework-connectivity-tiramisu-sources",
+    srcs: [
+        ":framework-connectivity-netstats-sources",
+        ":framework-connectivity-nsd-sources",
+        ":framework-connectivity-tiramisu-internal-sources",
+    ],
+    visibility: [
+        "//frameworks/base",
+    ],
+}
diff --git a/core/java/android/net/NetworkStats.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkStats.aidl
similarity index 100%
rename from core/java/android/net/NetworkStats.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkStats.aidl
diff --git a/core/java/android/net/NetworkTemplate.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkTemplate.aidl
similarity index 100%
rename from core/java/android/net/NetworkTemplate.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/NetworkTemplate.aidl
diff --git a/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl b/packages/ConnectivityT/framework-t/aidl-export/android/net/nsd/NsdServiceInfo.aidl
similarity index 100%
rename from packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
rename to packages/ConnectivityT/framework-t/aidl-export/android/net/nsd/NsdServiceInfo.aidl
diff --git a/core/java/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
similarity index 100%
rename from core/java/android/app/usage/NetworkStats.java
rename to packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
similarity index 100%
rename from core/java/android/app/usage/NetworkStatsManager.java
rename to packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
new file mode 100644
index 0000000..630f902e
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
@@ -0,0 +1,52 @@
+/*
+ * 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.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
+
+/**
+ * Class for performing registration for Connectivity services which are exposed via updatable APIs
+ * since Android T.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializerTiramisu {
+    private ConnectivityFrameworkInitializerTiramisu() {}
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers nsd services to
+     * {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called anywhere besides
+     * {@link SystemServiceRegistry}.
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(
+                Context.NSD_SERVICE,
+                NsdManager.class,
+                (context, serviceBinder) -> {
+                    INsdManager service = INsdManager.Stub.asInterface(serviceBinder);
+                    return new NsdManager(context, service);
+                }
+        );
+    }
+}
diff --git a/core/java/android/net/DataUsageRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.aidl
similarity index 100%
rename from core/java/android/net/DataUsageRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.aidl
diff --git a/core/java/android/net/DataUsageRequest.java b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
similarity index 100%
rename from core/java/android/net/DataUsageRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
diff --git a/core/java/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
similarity index 100%
rename from core/java/android/net/INetworkStatsService.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
diff --git a/core/java/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
similarity index 79%
rename from core/java/android/net/INetworkStatsSession.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index f13f2cb..dfedf66 100644
--- a/core/java/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -33,7 +33,17 @@
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
 
-    /** Return network layer usage summary per UID for traffic that matches template. */
+    /**
+     * Return network layer usage summary per UID for traffic that matches template.
+     *
+     * <p>The resulting {@code NetworkStats#getElapsedRealtime()} contains time delta between
+     * {@code start} and {@code end}.
+     *
+     * @param template - a predicate to filter netstats.
+     * @param start - start of the range, timestamp in milliseconds since the epoch.
+     * @param end - end of the range, timestamp in milliseconds since the epoch.
+     * @param includeTags - includes data usage tags if true.
+     */
     @UnsupportedAppUsage
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
     /** Return historical network layer stats for specific UID traffic that matches template. */
diff --git a/core/java/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
similarity index 100%
rename from core/java/android/net/NetworkIdentity.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
similarity index 96%
rename from services/core/java/com/android/server/net/NetworkIdentitySet.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 22ed781..abbebef 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.net;
+package android.net;
 
-import android.net.NetworkIdentity;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+
 import android.service.NetworkIdentitySetProto;
 import android.util.proto.ProtoOutputStream;
 
@@ -25,8 +26,6 @@
 import java.io.IOException;
 import java.util.HashSet;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-
 /**
  * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
  * active on that interface.
@@ -97,6 +96,9 @@
         }
     }
 
+    /**
+     * Method to serialize this object into a {@code DataOutput}.
+     */
     public void writeToStream(DataOutput out) throws IOException {
         out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
         out.writeInt(size());
@@ -179,6 +181,9 @@
         return ident.compareTo(anotherIdent);
     }
 
+    /**
+     * Method to dump this object into proto debug file.
+     */
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
diff --git a/core/java/android/net/NetworkStateSnapshot.aidl b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
similarity index 100%
rename from core/java/android/net/NetworkStateSnapshot.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
similarity index 100%
rename from core/java/android/net/NetworkStateSnapshot.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
diff --git a/core/java/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
similarity index 99%
rename from core/java/android/net/NetworkStats.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 46c83df..c7ffc19 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -220,8 +220,10 @@
     // TODO: move fields to "mVariable" notation
 
     /**
-     * {@link SystemClock#elapsedRealtime()} timestamp when this data was
+     * {@link SystemClock#elapsedRealtime()} timestamp in milliseconds when this data was
      * generated.
+     * It's a timestamps delta when {@link #subtract()},
+     * {@code INetworkStatsSession#getSummaryForAllUid()} methods are used.
      */
     private long elapsedRealtime;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
similarity index 97%
rename from services/core/java/com/android/server/net/NetworkStatsAccess.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
index d25eae4..3885a9e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.server.net;
+package android.net;
 
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.net.NetworkStats.UID_ALL;
@@ -37,7 +37,11 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-/** Utility methods for controlling access to network stats APIs. */
+/**
+ * Utility methods for controlling access to network stats APIs.
+ *
+ * @hide
+ */
 public final class NetworkStatsAccess {
     private NetworkStatsAccess() {}
 
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
similarity index 95%
rename from services/core/java/com/android/server/net/NetworkStatsCollection.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index df372b1..0d3b9ed 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.net;
+package android.net;
 
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -31,13 +31,7 @@
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
 import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
-import static com.android.server.net.NetworkStatsService.TAG;
 
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TrafficStats;
 import android.os.Binder;
 import android.service.NetworkStatsCollectionKeyProto;
 import android.service.NetworkStatsCollectionProto;
@@ -59,16 +53,15 @@
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
 
-import libcore.io.IoUtils;
-
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import libcore.io.IoUtils;
+
 import java.io.BufferedInputStream;
 import java.io.DataInput;
 import java.io.DataInputStream;
 import java.io.DataOutput;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -86,8 +79,11 @@
 /**
  * Collection of {@link NetworkStatsHistory}, stored based on combined key of
  * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
+ *
+ * @hide
  */
 public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
+    private static final String TAG = NetworkStatsCollection.class.getSimpleName();
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
@@ -335,7 +331,13 @@
 
     /**
      * Summarize all {@link NetworkStatsHistory} in this collection which match
-     * the requested parameters.
+     * the requested parameters across the requested range.
+     *
+     * @param template - a predicate for filtering netstats.
+     * @param start - start of the range, timestamp in milliseconds since the epoch.
+     * @param end - end of the range, timestamp in milliseconds since the epoch.
+     * @param accessLevel - caller access level.
+     * @param callerUid - caller UID.
      */
     public NetworkStats getSummary(NetworkTemplate template, long start, long end,
             @NetworkStatsAccess.Level int accessLevel, int callerUid) {
@@ -361,8 +363,8 @@
                 entry.uid = key.uid;
                 entry.set = key.set;
                 entry.tag = key.tag;
-                entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
-                        DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
+                entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork()
+                        ? DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
                 entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
                 entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
                 entry.rxBytes = historyEntry.rxBytes;
@@ -516,6 +518,12 @@
         }
     }
 
+    /**
+     * Read legacy network summary statistics file format into the collection,
+     * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
+     *
+     * @deprecated
+     */
     @Deprecated
     public void readLegacyNetwork(File file) throws IOException {
         final AtomicFile inputFile = new AtomicFile(file);
@@ -555,6 +563,12 @@
         }
     }
 
+    /**
+     * Read legacy Uid statistics file format into the collection,
+     * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
+     *
+     * @deprecated
+     */
     @Deprecated
     public void readLegacyUid(File file, boolean onlyTags) throws IOException {
         final AtomicFile inputFile = new AtomicFile(file);
@@ -769,19 +783,19 @@
         public final int set;
         public final int tag;
 
-        private final int hashCode;
+        private final int mHashCode;
 
-        public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
+        Key(NetworkIdentitySet ident, int uid, int set, int tag) {
             this.ident = ident;
             this.uid = uid;
             this.set = set;
             this.tag = tag;
-            hashCode = Objects.hash(ident, uid, set, tag);
+            mHashCode = Objects.hash(ident, uid, set, tag);
         }
 
         @Override
         public int hashCode() {
-            return hashCode;
+            return mHashCode;
         }
 
         @Override
diff --git a/core/java/android/net/NetworkStatsHistory.aidl b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.aidl
similarity index 100%
rename from core/java/android/net/NetworkStatsHistory.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.aidl
diff --git a/core/java/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
similarity index 97%
rename from core/java/android/net/NetworkStatsHistory.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index f413063..a875e1a 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -526,6 +526,13 @@
     /**
      * Return interpolated data usage across the requested range. Interpolates
      * across buckets, so values may be rounded slightly.
+     *
+     * <p>If the active bucket is not completed yet, it returns the proportional value of it
+     * based on its duration and the {@code end} param.
+     *
+     * @param start - start of the range, timestamp in milliseconds since the epoch.
+     * @param end - end of the range, timestamp in milliseconds since the epoch.
+     * @param recycle - entry instance for performance, could be null.
      */
     @UnsupportedAppUsage
     public Entry getValues(long start, long end, Entry recycle) {
@@ -535,6 +542,11 @@
     /**
      * Return interpolated data usage across the requested range. Interpolates
      * across buckets, so values may be rounded slightly.
+     *
+     * @param start - start of the range, timestamp in milliseconds since the epoch.
+     * @param end - end of the range, timestamp in milliseconds since the epoch.
+     * @param now - current timestamp in milliseconds since the epoch (wall clock).
+     * @param recycle - entry instance for performance, could be null.
      */
     @UnsupportedAppUsage
     public Entry getValues(long start, long end, long now, Entry recycle) {
diff --git a/core/java/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
similarity index 91%
rename from core/java/android/net/NetworkTemplate.java
rename to packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index c906a13..8b9c14d 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -44,16 +44,10 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.BackupUtils;
-import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
@@ -69,19 +63,6 @@
 public class NetworkTemplate implements Parcelable {
     private static final String TAG = "NetworkTemplate";
 
-    /**
-     * Initial Version of the backup serializer.
-     */
-    public static final int BACKUP_VERSION_1_INIT = 1;
-    /**
-     * Version of the backup serializer that added carrier template support.
-     */
-    public static final int BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
-    /**
-     * Current Version of the Backup Serializer.
-     */
-    private static final int BACKUP_VERSION = BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
-
     public static final int MATCH_MOBILE = 1;
     public static final int MATCH_WIFI = 4;
     public static final int MATCH_ETHERNET = 5;
@@ -849,58 +830,4 @@
             return new NetworkTemplate[size];
         }
     };
-
-    public byte[] getBytesForBackup() throws IOException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DataOutputStream out = new DataOutputStream(baos);
-
-        if (!isPersistable()) {
-            Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
-        }
-
-        out.writeInt(BACKUP_VERSION);
-
-        out.writeInt(mMatchRule);
-        BackupUtils.writeString(out, mSubscriberId);
-        BackupUtils.writeString(out, mNetworkId);
-        out.writeInt(mMetered);
-        out.writeInt(mSubscriberIdMatchRule);
-
-        return baos.toByteArray();
-    }
-
-    public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
-            throws IOException, BackupUtils.BadVersionException {
-        int version = in.readInt();
-        if (version < BACKUP_VERSION_1_INIT || version > BACKUP_VERSION) {
-            throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
-        }
-
-        int matchRule = in.readInt();
-        String subscriberId = BackupUtils.readString(in);
-        String networkId = BackupUtils.readString(in);
-
-        final int metered;
-        final int subscriberIdMatchRule;
-        if (version >= BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
-            metered = in.readInt();
-            subscriberIdMatchRule = in.readInt();
-        } else {
-            // For backward compatibility, fill the missing filters from match rules.
-            metered = (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD
-                    || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
-            subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
-        }
-
-        try {
-            return new NetworkTemplate(matchRule,
-                    subscriberId, new String[] { subscriberId },
-                    networkId, metered, NetworkStats.ROAMING_ALL,
-                    NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
-                    NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
-        } catch (IllegalArgumentException e) {
-            throw new BackupUtils.BadVersionException(
-                    "Restored network template contains unknown match rule " + matchRule, e);
-        }
-    }
 }
diff --git a/core/java/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
similarity index 100%
rename from core/java/android/net/TrafficStats.java
rename to packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
diff --git a/core/java/android/net/UnderlyingNetworkInfo.aidl b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.aidl
similarity index 100%
rename from core/java/android/net/UnderlyingNetworkInfo.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.aidl
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
similarity index 100%
rename from core/java/android/net/UnderlyingNetworkInfo.java
rename to packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProvider.aidl
similarity index 100%
rename from core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProvider.aidl
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
similarity index 100%
rename from core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
similarity index 100%
rename from core/java/android/net/netstats/provider/NetworkStatsProvider.java
rename to packages/ConnectivityT/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManager.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManager.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl b/packages/ConnectivityT/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
diff --git a/packages/Nsd/framework/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/NsdManager.java
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
diff --git a/packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
similarity index 100%
rename from packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java
rename to packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
new file mode 100644
index 0000000..6a64910
--- /dev/null
+++ b/packages/ConnectivityT/service/Android.bp
@@ -0,0 +1,63 @@
+//
+// 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// NetworkStats related libraries.
+
+filegroup {
+    name: "services.connectivity-netstats-sources",
+    srcs: [
+        "src/com/android/server/net/NetworkIdentity*.java",
+        "src/com/android/server/net/NetworkStats*.java",
+    ],
+    path: "src",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+// Nsd related libraries.
+
+filegroup {
+    name: "services.connectivity-nsd-sources",
+    srcs: [
+        "src/com/android/server/INativeDaemon*.java",
+        "src/com/android/server/NativeDaemon*.java",
+        "src/com/android/server/Nsd*.java",
+    ],
+    path: "src",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
+// Connectivity-T common libraries.
+
+filegroup {
+    name: "services.connectivity-tiramisu-sources",
+    srcs: [
+        ":services.connectivity-netstats-sources",
+        ":services.connectivity-nsd-sources",
+    ],
+    path: "src",
+    visibility: [
+        "//frameworks/base/services/core",
+    ],
+}
\ No newline at end of file
diff --git a/packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java b/packages/ConnectivityT/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
rename to packages/ConnectivityT/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnector.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnector.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnectorException.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonConnectorException.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonEvent.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonEvent.java
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java b/packages/ConnectivityT/service/src/com/android/server/NativeDaemonTimeoutException.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java
rename to packages/ConnectivityT/service/src/com/android/server/NativeDaemonTimeoutException.java
diff --git a/packages/Nsd/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
similarity index 100%
rename from packages/Nsd/service/src/com/android/server/NsdService.java
rename to packages/ConnectivityT/service/src/com/android/server/NsdService.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsFactory.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
similarity index 98%
rename from services/core/java/com/android/server/net/NetworkStatsObservers.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index 2564dae..1a0866d 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -22,7 +22,10 @@
 
 import android.app.usage.NetworkStatsManager;
 import android.net.DataUsageRequest;
+import android.net.NetworkIdentitySet;
 import android.net.NetworkStats;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
similarity index 98%
rename from services/core/java/com/android/server/net/NetworkStatsRecorder.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index 978ae87..5e27c77 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -21,8 +21,11 @@
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
+import android.net.NetworkIdentitySet;
 import android.net.NetworkStats;
 import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
similarity index 99%
rename from services/core/java/com/android/server/net/NetworkStatsService.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index c876d41..2beca73 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -96,11 +96,14 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
 import android.net.NetworkStats;
 import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsAccess;
+import android.net.NetworkStatsCollection;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
similarity index 100%
rename from services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
rename to packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
diff --git a/packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java b/packages/ConnectivityT/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
similarity index 100%
rename from packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
rename to packages/ConnectivityT/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
diff --git a/packages/Nsd/OWNERS b/packages/Nsd/OWNERS
deleted file mode 100644
index 4862377..0000000
--- a/packages/Nsd/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file
diff --git a/packages/Nsd/framework/Android.bp b/packages/Nsd/framework/Android.bp
deleted file mode 100644
index 2363a9f..0000000
--- a/packages/Nsd/framework/Android.bp
+++ /dev/null
@@ -1,54 +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 {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-    name: "framework-connectivity-nsd-internal-sources",
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.aidl",
-    ],
-    path: "src",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-filegroup {
-    name: "framework-connectivity-nsd-aidl-export-sources",
-    srcs: [
-        "aidl-export/**/*.aidl",
-    ],
-    path: "aidl-export",
-    visibility: [
-        "//visibility:private",
-    ],
-}
-
-filegroup {
-    name: "framework-connectivity-nsd-sources",
-    srcs: [
-        ":framework-connectivity-nsd-internal-sources",
-        ":framework-connectivity-nsd-aidl-export-sources",
-    ],
-    visibility: [
-        "//frameworks/base",
-    ],
-}
diff --git a/packages/Nsd/service/Android.bp b/packages/Nsd/service/Android.bp
deleted file mode 100644
index 529f58d..0000000
--- a/packages/Nsd/service/Android.bp
+++ /dev/null
@@ -1,31 +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 {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-    name: "services.connectivity-nsd-sources",
-    srcs: [
-        "src/**/*.java",
-    ],
-    path: "src",
-    visibility: [
-        "//frameworks/base/services/core",
-    ],
-}
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 36c2bda..7f17d26 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
@@ -37,11 +36,10 @@
         if (BuildCompatUtils.isAtLeastS()) {
             final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
             intent.setPackage(PACKAGE_NAME_SETTINGS);
-            final ResolveInfo resolveInfo =
-                    context.getPackageManager().resolveActivity(intent, 0 /* flags */);
-            return resolveInfo != null
-                    && resolveInfo.activityInfo != null
-                    && resolveInfo.activityInfo.enabled;
+            final boolean isEmbeddingActivityEnabled =
+                    intent.resolveActivity(context.getPackageManager()) != null;
+
+            return isEmbeddingActivityEnabled;
         }
         return false;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 6d576a1..63153f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -159,7 +159,7 @@
         }
 
         public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
-                @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) {
+                @PackageManager.ResolveInfoFlagsBits int flags, @UserIdInt int userId) {
             List<ResolveInfo> resolveInfos = new ArrayList<>();
             ResolveInfo resolveInfo = new ResolveInfo();
             resolveInfo.activityInfo = new ActivityInfo();
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e5b5285..23c53a1 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -200,6 +200,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.CREATE_USERS" />
+    <uses-permission android:name="android.permission.QUERY_USERS" />
     <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
     <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a1b3df8..1e9a41e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -218,7 +218,6 @@
 
     <!-- notifications & DND access -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
-    <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
new file mode 100644
index 0000000..230a256
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+    <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+        <path android:name="_R_G_L_2_G_D_0_P_0"
+              android:fillColor="#FF000000"
+              android:fillAlpha="1"
+              android:fillType="nonZero"
+              android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+    </group>
+    <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+        <path android:name="_R_G_L_1_G_D_0_P_0"
+              android:strokeColor="#FF000000"
+              android:strokeLineCap="round"
+              android:strokeLineJoin="round"
+              android:strokeWidth="1.5"
+              android:strokeAlpha="1"
+              android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+    </group>
+    <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+        <path android:name="_R_G_L_0_G_D_0_P_0"
+              android:strokeColor="#FF000000"
+              android:strokeLineCap="round"
+              android:strokeLineJoin="round"
+              android:strokeWidth="1.5"
+              android:strokeAlpha="1"
+              android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index c58e2e3..67a70bb 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -50,6 +50,11 @@
         android:state_first="true"
         android:state_single="true"
         android:drawable="@drawable/ic_lock_aod" />
+    <item
+        android:id="@+id/unlocked_aod"
+        android:state_last="true"
+        android:state_single="true"
+        android:drawable="@drawable/ic_unlocked_aod" />
 
     <item
         android:id="@+id/no_icon"
@@ -79,4 +84,14 @@
         android:fromId="@id/locked"
         android:toId="@id/locked_aod"
         android:drawable="@drawable/lock_ls_to_aod" />
+
+    <transition
+        android:fromId="@id/unlocked_aod"
+        android:toId="@id/unlocked"
+        android:drawable="@drawable/unlocked_aod_to_ls" />
+
+    <transition
+        android:fromId="@id/unlocked"
+        android:toId="@id/unlocked_aod"
+        android:drawable="@drawable/unlocked_ls_to_aod" />
 </animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
new file mode 100644
index 0000000..3b59ba8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
@@ -0,0 +1,133 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+                </group>
+                <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="333" android:startOffset="0" android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueTo="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
new file mode 100644
index 0000000..1c6d0b5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
@@ -0,0 +1,136 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+                </group>
+                <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2.5"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 3c9e44e..89690e8 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -73,6 +73,9 @@
         android:accessibilityLiveRegion="polite"
         android:singleLine="true"
         android:ellipsize="marquee"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:scrollHorizontally="true"
+        android:fadingEdge="horizontal"
         android:textColor="@color/biometric_dialog_gray"/>
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 332dc6ee..a2abdb2 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,8 +26,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:minHeight="64dp"
-            android:paddingTop="12dp"
             android:textAppearance="?android:attr/textAppearanceButton"
-            android:gravity="top|center_horizontal"
+            android:gravity="center"
             android:text="@string/empty_shade_text"/>
 </com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9d4c2c3..3412722 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -78,7 +78,7 @@
     <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
 
     <!-- UDFPS colors -->
-    <color name="udfps_enroll_icon">#ffffff</color> <!-- 100% white -->
+    <color name="udfps_enroll_icon">#7DA7F1</color>
 
     <color name="GM2_green_500">#FF41Af6A</color>
     <color name="GM2_blue_500">#5195EA</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8cad5b3..fc28f09 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -134,10 +134,10 @@
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
     <!-- UDFPS colors -->
-    <color name="udfps_enroll_icon">#000000</color>                         <!-- 100% black -->
-    <color name="udfps_moving_target_fill">#cc4285f4</color>                <!-- 80% blue -->
-    <color name="udfps_enroll_progress">#ff669DF6</color>                   <!-- blue 400 -->
-    <color name="udfps_enroll_progress_help">#ffEE675C</color>              <!-- red 400 -->
+    <color name="udfps_enroll_icon">#7DA7F1</color>
+    <color name="udfps_moving_target_fill">#475670</color>
+    <color name="udfps_enroll_progress">#7DA7F1</color>
+    <color name="udfps_enroll_progress_help">#ffEE675C</color>
 
     <!-- Global screenshot actions -->
     <color name="screenshot_button_ripple">#1f000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index df8fc22..3695286 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -598,17 +598,6 @@
         280
     </integer>
 
-    <!-- Haptic feedback intensity for ticks used for the udfps dwell time -->
-    <item name="config_udfpsTickIntensity" translatable="false" format="float"
-          type="dimen">.5</item>
-
-    <!-- Haptic feedback delay between ticks used for udfps dwell time -->
-    <integer name="config_udfpsTickDelay" translatable="false">25</integer>
-
-    <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK
-         else uses VibrationEffect.Composition.PRIMITIVE_TICK -->
-    <bool name="config_udfpsUseLowTick">true</bool>
-
     <!-- package name of a built-in camera app to use to restrict implicit intent resolution
          when the double-press power gesture is used. Ignored if empty. -->
     <string translatable="false" name="config_cameraGesturePackage"></string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 7d0fb5d..567e7aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -53,8 +53,8 @@
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, positionX, positionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
+        return newPipSurfaceTransaction(positionX, positionY,
+                mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scale(
@@ -70,8 +70,8 @@
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, positionX, positionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
+        return newPipSurfaceTransaction(positionX, positionY,
+                mTmpFloat9, degree, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -93,8 +93,8 @@
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, left, top)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
+        return newPipSurfaceTransaction(left, top,
+                mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -125,8 +125,7 @@
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, adjustedPositionX, adjustedPositionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                adjustedPositionX, adjustedPositionY,
+        return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY,
                 mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
     }
 
@@ -137,6 +136,17 @@
         return mCornerRadius * scale;
     }
 
+    private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction(
+            float posX, float posY, float[] float9, float rotation, float cornerRadius,
+            Rect windowCrop) {
+        return new PictureInPictureSurfaceTransaction.Builder()
+                .setPosition(posX, posY)
+                .setTransform(float9, rotation)
+                .setCornerRadius(cornerRadius)
+                .setWindowCrop(windowCrop)
+                .build();
+    }
+
     /** @return {@link SurfaceControl.Transaction} instance with vsync-id */
     public static SurfaceControl.Transaction newSurfaceControlTransaction() {
         final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
index 443c1e1..5c37ecce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
@@ -22,7 +22,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlagsBits;
 import android.content.pm.ResolveInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -73,7 +73,7 @@
     /**
      * Determine the best Activity to perform for a given Intent.
      */
-    public ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags) {
+    public ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlagsBits int flags) {
         final String resolvedType =
                 intent.resolveTypeIfNeeded(AppGlobals.getInitialApplication().getContentResolver());
         try {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 30db136..c1d9d0d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -65,6 +65,7 @@
     public final Rect localBounds;
     public final Rect sourceContainerBounds;
     public final Rect screenSpaceBounds;
+    public final Rect startScreenSpaceBounds;
     public final boolean isNotInRecents;
     public final Rect contentInsets;
     public final ActivityManager.RunningTaskInfo taskInfo;
@@ -88,6 +89,7 @@
         localBounds = app.localBounds;
         sourceContainerBounds = app.sourceContainerBounds;
         screenSpaceBounds = app.screenSpaceBounds;
+        startScreenSpaceBounds = screenSpaceBounds;
         prefixOrderIndex = app.prefixOrderIndex;
         isNotInRecents = app.isNotInRecents;
         contentInsets = app.contentInsets;
@@ -219,6 +221,8 @@
         localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
         sourceContainerBounds = null;
         screenSpaceBounds = new Rect(change.getEndAbsBounds());
+        startScreenSpaceBounds = new Rect(change.getStartAbsBounds());
+
         prefixOrderIndex = order;
         // TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
         contentInsets = new Rect(0, 0, 0, 0);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index c628d44..032da78 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -89,7 +89,6 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardBypassController mBypassController;
 
-    private int mLargeClockTopMargin = 0;
     private int mKeyguardClockTopMargin = 0;
 
     /**
@@ -276,16 +275,15 @@
     }
 
     private void updateClockLayout() {
+        int largeClockTopMargin = 0;
         if (mSmartspaceController.isEnabled()) {
-            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
-                    MATCH_PARENT);
-            mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+            largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
                     R.dimen.keyguard_large_clock_top_margin);
-            lp.topMargin = mLargeClockTopMargin;
-            mLargeClockFrame.setLayoutParams(lp);
-        } else {
-            mLargeClockTopMargin = 0;
         }
+        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
+                MATCH_PARENT);
+        lp.topMargin = largeClockTopMargin;
+        mLargeClockFrame.setLayoutParams(lp);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 95567ec..67ef02a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,6 +19,8 @@
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
 
+import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
+
 import static java.lang.Integer.max;
 
 import android.animation.Animator;
@@ -71,7 +73,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
-import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
@@ -124,7 +126,7 @@
     @VisibleForTesting
     KeyguardSecurityViewFlipper mSecurityViewFlipper;
     private GlobalSettings mGlobalSettings;
-    private FalsingCollector mFalsingCollector;
+    private FalsingManager mFalsingManager;
     private UserSwitcherController mUserSwitcherController;
     private AlertDialog mAlertDialog;
     private boolean mSwipeUpToRetry;
@@ -300,7 +302,7 @@
         setupViewMode();
     }
 
-    void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingCollector falsingCollector,
+    void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingManager falsingManager,
             UserSwitcherController userSwitcherController) {
         if (mCurrentMode == mode) return;
         Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
@@ -318,7 +320,7 @@
                 mViewMode = new DefaultViewMode();
         }
         mGlobalSettings = globalSettings;
-        mFalsingCollector = falsingCollector;
+        mFalsingManager = falsingManager;
         mUserSwitcherController = userSwitcherController;
         setupViewMode();
     }
@@ -338,11 +340,11 @@
 
     private void setupViewMode() {
         if (mSecurityViewFlipper == null || mGlobalSettings == null
-                || mFalsingCollector == null || mUserSwitcherController == null) {
+                || mFalsingManager == null || mUserSwitcherController == null) {
             return;
         }
 
-        mViewMode.init(this, mGlobalSettings, mSecurityViewFlipper, mFalsingCollector,
+        mViewMode.init(this, mGlobalSettings, mSecurityViewFlipper, mFalsingManager,
                 mUserSwitcherController);
     }
 
@@ -695,7 +697,7 @@
 
         default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
                 @NonNull KeyguardSecurityViewFlipper viewFlipper,
-                @NonNull FalsingCollector falsingCollector,
+                @NonNull FalsingManager falsingManager,
                 @NonNull UserSwitcherController userSwitcherController) {};
 
         /** Reinitialize the location */
@@ -732,7 +734,7 @@
         @Override
         public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
                 @NonNull KeyguardSecurityViewFlipper viewFlipper,
-                @NonNull FalsingCollector falsingCollector,
+                @NonNull FalsingManager falsingManager,
                 @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
             mViewFlipper = viewFlipper;
@@ -760,18 +762,18 @@
         private KeyguardSecurityViewFlipper mViewFlipper;
         private ImageView mUserIconView;
         private TextView mUserSwitcher;
-        private FalsingCollector mFalsingCollector;
+        private FalsingManager mFalsingManager;
         private UserSwitcherController mUserSwitcherController;
         private KeyguardUserSwitcherPopupMenu mPopup;
 
         @Override
         public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
                 @NonNull KeyguardSecurityViewFlipper viewFlipper,
-                @NonNull FalsingCollector falsingCollector,
+                @NonNull FalsingManager falsingManager,
                 @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
             mViewFlipper = viewFlipper;
-            mFalsingCollector = falsingCollector;
+            mFalsingManager = falsingManager;
             mUserSwitcherController = userSwitcherController;
 
             if (mUserSwitcherViewGroup == null) {
@@ -865,30 +867,26 @@
             }
 
             anchor.setClickable(true);
-            anchor.setOnTouchListener((v, ev) -> {
-                if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                    mFalsingCollector.avoidGesture();
-                    mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(),
-                            mFalsingCollector);
-                    mPopup.setAnchorView(anchor);
-                    mPopup.setAdapter(adapter);
-                    mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                            public void onItemClick(AdapterView parent, View view, int pos,
-                                    long id) {
-                                mFalsingCollector.avoidGesture();
+            anchor.setOnClickListener((v) -> {
+                if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
 
-                                // - 1 to account for the header view
-                                UserRecord user = adapter.getItem(pos - 1);
-                                if (!user.isCurrent) {
-                                    adapter.onUserListItemClicked(user);
-                                }
-                                mPopup.dismiss();
-                                mPopup = null;
+                mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(), mFalsingManager);
+                mPopup.setAnchorView(anchor);
+                mPopup.setAdapter(adapter);
+                mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                        public void onItemClick(AdapterView parent, View view, int pos, long id) {
+                            if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
+
+                            // - 1 to account for the header view
+                            UserRecord user = adapter.getItem(pos - 1);
+                            if (!user.isCurrent) {
+                                adapter.onUserListItemClicked(user);
                             }
-                        });
-                    mPopup.show();
-                }
-                return true;
+                            mPopup.dismiss();
+                            mPopup = null;
+                        }
+                    });
+                mPopup.show();
             });
         }
 
@@ -935,7 +933,7 @@
         @Override
         public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
                 @NonNull KeyguardSecurityViewFlipper viewFlipper,
-                @NonNull FalsingCollector falsingCollector,
+                @NonNull FalsingManager falsingManager,
                 @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
             mViewFlipper = viewFlipper;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 6b73a32..2fb2211 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -51,6 +51,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -79,6 +80,7 @@
     private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
     private final FalsingCollector mFalsingCollector;
+    private final FalsingManager mFalsingManager;
     private final UserSwitcherController mUserSwitcherController;
     private final GlobalSettings mGlobalSettings;
 
@@ -234,6 +236,7 @@
             KeyguardSecurityViewFlipperController securityViewFlipperController,
             ConfigurationController configurationController,
             FalsingCollector falsingCollector,
+            FalsingManager falsingManager,
             UserSwitcherController userSwitcherController,
             GlobalSettings globalSettings) {
         super(view);
@@ -250,6 +253,7 @@
         mConfigurationController = configurationController;
         mLastOrientation = getResources().getConfiguration().orientation;
         mFalsingCollector = falsingCollector;
+        mFalsingManager = falsingManager;
         mUserSwitcherController = userSwitcherController;
         mGlobalSettings = globalSettings;
     }
@@ -519,7 +523,7 @@
             mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
         }
 
-        mView.initMode(mode, mGlobalSettings, mFalsingCollector, mUserSwitcherController);
+        mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController);
     }
 
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
@@ -609,6 +613,7 @@
         private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
         private final ConfigurationController mConfigurationController;
         private final FalsingCollector mFalsingCollector;
+        private final FalsingManager mFalsingManager;
         private final GlobalSettings mGlobalSettings;
         private final UserSwitcherController mUserSwitcherController;
 
@@ -625,6 +630,7 @@
                 KeyguardSecurityViewFlipperController securityViewFlipperController,
                 ConfigurationController configurationController,
                 FalsingCollector falsingCollector,
+                FalsingManager falsingManager,
                 UserSwitcherController userSwitcherController,
                 GlobalSettings globalSettings) {
             mView = view;
@@ -638,6 +644,7 @@
             mSecurityViewFlipperController = securityViewFlipperController;
             mConfigurationController = configurationController;
             mFalsingCollector = falsingCollector;
+            mFalsingManager = falsingManager;
             mGlobalSettings = globalSettings;
             mUserSwitcherController = userSwitcherController;
         }
@@ -648,8 +655,8 @@
                     mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController, mFalsingCollector, mUserSwitcherController,
-                    mGlobalSettings);
+                    mConfigurationController, mFalsingCollector, mFalsingManager,
+                    mUserSwitcherController, mGlobalSettings);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
index ca31b40d..dfb4667 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -26,14 +26,14 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
 
 /**
  * Custom user-switcher for use on the bouncer.
  */
 public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
     private Context mContext;
-    private FalsingCollector mFalsingCollector;
+    private FalsingManager mFalsingManager;
     private int mLastHeight = -1;
     private View.OnLayoutChangeListener mLayoutListener = (v, l, t, r, b, ol, ot, or, ob) -> {
         int height = -v.getMeasuredHeight() + getAnchorView().getHeight();
@@ -45,10 +45,10 @@
     };
 
     public KeyguardUserSwitcherPopupMenu(@NonNull Context context,
-            @NonNull FalsingCollector falsingCollector) {
+            @NonNull FalsingManager falsingManager) {
         super(context);
         mContext = context;
-        mFalsingCollector = falsingCollector;
+        mFalsingManager = falsingManager;
         Resources res = mContext.getResources();
         setBackgroundDrawable(
                 res.getDrawable(R.drawable.keyguard_user_switcher_popup_bg, context.getTheme()));
@@ -76,7 +76,7 @@
 
         listView.setOnTouchListener((v, ev) -> {
             if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                mFalsingCollector.avoidGesture();
+                return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
             }
             return false;
         });
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1f5303f..cd57af4 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,12 +35,10 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Process;
 import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -68,8 +66,6 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
-import com.airbnb.lottie.LottieAnimationView;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -101,10 +97,8 @@
     @NonNull private final AccessibilityManager mAccessibilityManager;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final DelayableExecutor mExecutor;
-    @NonNull private final LayoutInflater mLayoutInflater;
     private boolean mUdfpsEnrolled;
 
-    @Nullable private LottieAnimationView mAodFp;
     @NonNull private final AnimatedStateListDrawable mIcon;
 
     @NonNull private CharSequence mUnlockedLabel;
@@ -116,7 +110,6 @@
     private VelocityTracker mVelocityTracker;
     // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
     private int mActivePointerId = -1;
-    private VibrationEffect mTick;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
@@ -140,7 +133,7 @@
 
     // for udfps when strong auth is required or unlocked on AOD
     private boolean mShowAodLockIcon;
-    private boolean mShowAODFpIcon;
+    private boolean mShowAodUnlockedIcon;
     private final int mMaxBurnInOffsetX;
     private final int mMaxBurnInOffsetY;
     private float mInterpolatedDarkAmount;
@@ -163,8 +156,7 @@
             @NonNull @Main DelayableExecutor executor,
             @Nullable Vibrator vibrator,
             @Nullable AuthRippleController authRippleController,
-            @NonNull @Main Resources resources,
-            @NonNull LayoutInflater inflater
+            @NonNull @Main Resources resources
     ) {
         super(view);
         mStatusBarStateController = statusBarStateController;
@@ -178,7 +170,6 @@
         mExecutor = executor;
         mVibrator = vibrator;
         mAuthRippleController = authRippleController;
-        mLayoutInflater = inflater;
 
         mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
         mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
@@ -260,11 +251,12 @@
         }
 
         boolean wasShowingUnlock = mShowUnlockIcon;
-        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon;
+        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
+                && !mShowAodUnlockedIcon && !mShowAodLockIcon;
         mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
                 && (!mUdfpsEnrolled || !mRunningFPS);
         mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
-        mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
+        mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
         mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
 
         final CharSequence prevContentDescription = mView.getContentDescription();
@@ -281,14 +273,9 @@
             mView.updateIcon(ICON_UNLOCK, false);
             mView.setContentDescription(mUnlockedLabel);
             mView.setVisibility(View.VISIBLE);
-        } else if (mShowAODFpIcon) {
-            // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset),
-            // this state shows a transparent view
-            mView.setContentDescription(null);
-            mAodFp.setVisibility(View.VISIBLE);
-            mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
-
-            mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon
+        } else if (mShowAodUnlockedIcon) {
+            mView.updateIcon(ICON_UNLOCK, true);
+            mView.setContentDescription(mUnlockedLabel);
             mView.setVisibility(View.VISIBLE);
         } else if (mShowAodLockIcon) {
             if (wasShowingUnlock) {
@@ -304,11 +291,6 @@
             mView.setContentDescription(null);
         }
 
-        if (!mShowAODFpIcon && mAodFp != null) {
-            mAodFp.setVisibility(View.INVISIBLE);
-            mAodFp.setContentDescription(null);
-        }
-
         if (!Objects.equals(prevContentDescription, mView.getContentDescription())
                 && mView.getContentDescription() != null) {
             mView.announceForAccessibility(mView.getContentDescription());
@@ -396,7 +378,7 @@
         pw.println();
         pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println(" mShowLockIcon: " + mShowLockIcon);
-        pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
+        pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
         pw.println("  mIsDozing: " + mIsDozing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
         pw.println("  mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -425,13 +407,6 @@
                         - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
         float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
 
-        if (mAodFp != null) {
-            mAodFp.setTranslationX(offsetX);
-            mAodFp.setTranslationY(offsetY);
-            mAodFp.setProgress(progress);
-            mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
-        }
-
         mView.setTranslationX(offsetX);
         mView.setTranslationY(offsetY);
     }
@@ -444,10 +419,6 @@
         mView.setUseBackground(mUdfpsSupported);
 
         mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
-        if (!wasUdfpsEnrolled && mUdfpsEnrolled && mAodFp == null) {
-            mLayoutInflater.inflate(R.layout.udfps_aod_lock_icon, mView);
-            mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
-        }
         if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
             updateVisibility();
         }
@@ -594,15 +565,11 @@
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_HOVER_ENTER:
                 if (mVibrator != null && !mDownDetected) {
-                    if (mTick == null) {
-                        mTick = UdfpsController.lowTick(getContext(), true,
-                                LONG_PRESS_TIMEOUT);
-                    }
                     mVibrator.vibrate(
                             Process.myUid(),
                             getContext().getOpPackageName(),
-                            mTick,
-                            "lock-icon-tick",
+                            UdfpsController.EFFECT_CLICK,
+                            "lock-icon-down",
                             TOUCH_VIBRATION_ATTRIBUTES);
                 }
 
@@ -715,8 +682,7 @@
 
     private boolean inLockIconArea(MotionEvent event) {
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                && (mView.getVisibility() == View.VISIBLE
-                || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE));
+                && mView.getVisibility() == View.VISIBLE;
     }
 
     private boolean isActionable() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 251c1e6..2767904 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -121,7 +121,7 @@
                     .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
-                    .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()))
+                    .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
                     .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
@@ -141,7 +141,7 @@
                     .setStartingSurface(Optional.ofNullable(null))
                     .setTaskSurfaceHelper(Optional.ofNullable(null))
                     .setRecentTasks(Optional.ofNullable(null))
-                    .setSizeCompatUI(Optional.ofNullable(null))
+                    .setCompatUI(Optional.ofNullable(null))
                     .setDragAndDrop(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index f11dc93..e35b558 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,6 +636,7 @@
         mIndicatorView.setText(message);
         mIndicatorView.setTextColor(mTextColorError);
         mIndicatorView.setVisibility(View.VISIBLE);
+        mIndicatorView.setSelected(true);
         mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
 
         Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index fb4616a..07aec69 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,6 +23,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.util.ViewController;
@@ -44,6 +45,7 @@
         extends ViewController<T> implements Dumpable {
     @NonNull final StatusBarStateController mStatusBarStateController;
     @NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
+    @NonNull final SystemUIDialogManager mDialogManager;
     @NonNull final DumpManager mDumpManger;
 
     boolean mNotificationShadeVisible;
@@ -52,10 +54,12 @@
             T view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager dialogManager,
             @NonNull DumpManager dumpManager) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mPanelExpansionStateManager = panelExpansionStateManager;
+        mDialogManager = dialogManager;
         mDumpManger = dumpManager;
     }
 
@@ -64,12 +68,14 @@
     @Override
     protected void onViewAttached() {
         mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
+        mDialogManager.registerListener(mDialogListener);
         mDumpManger.registerDumpable(getDumpTag(), this);
     }
 
     @Override
     protected void onViewDetached() {
         mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
+        mDialogManager.registerListener(mDialogListener);
         mDumpManger.unregisterDumpable(getDumpTag());
     }
 
@@ -95,7 +101,8 @@
      * authentication.
      */
     boolean shouldPauseAuth() {
-        return mNotificationShadeVisible;
+        return mNotificationShadeVisible
+                || mDialogManager.shouldHideAffordance();
     }
 
     /**
@@ -189,4 +196,7 @@
             updatePauseAuth();
         }
     };
+
+    private final SystemUIDialogManager.Listener mDialogListener =
+            (shouldHide) -> updatePauseAuth();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 894b295..3732100 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -30,8 +31,10 @@
             @NonNull UdfpsBpView view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager,
+                systemUIDialogManager, dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c0dcbb0..1da9f21 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,7 +47,6 @@
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -71,6 +70,7 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -119,6 +119,7 @@
     @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final DumpManager mDumpManager;
+    @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Nullable private final Vibrator mVibrator;
     @NonNull private final FalsingManager mFalsingManager;
@@ -165,9 +166,6 @@
     private boolean mAttemptedToDismissKeyguard;
     private Set<Callback> mCallbacks = new HashSet<>();
 
-    private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
-    private final VibrationEffect mTick;
-
     @VisibleForTesting
     public static final VibrationAttributes VIBRATION_ATTRIBUTES =
             new VibrationAttributes.Builder()
@@ -281,9 +279,6 @@
                     return;
                 }
                 mGoodCaptureReceived = true;
-                if (mVibrator != null) {
-                    mVibrator.cancel();
-                }
                 mView.stopIllumination();
                 if (mServerRequest != null) {
                     mServerRequest.onAcquiredGood();
@@ -558,7 +553,8 @@
             @Main Handler mainHandler,
             @NonNull ConfigurationController configurationController,
             @NonNull SystemClock systemClock,
-            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            @NonNull SystemUIDialogManager dialogManager) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -573,6 +569,7 @@
         mKeyguardStateController = keyguardStateController;
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mDumpManager = dumpManager;
+        mDialogManager = dialogManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
@@ -585,7 +582,6 @@
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION);
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -621,47 +617,6 @@
     }
 
     /**
-     * Returns the continuous low tick effect that starts playing on the udfps finger-down event.
-     */
-    public static VibrationEffect lowTick(
-            Context context,
-            boolean useShortRampUp,
-            long duration
-    ) {
-        boolean useLowTickDefault = context.getResources()
-                .getBoolean(R.bool.config_udfpsUseLowTick);
-        int primitiveTick = DEFAULT_TICK;
-        if (Settings.Global.getFloat(
-                context.getContentResolver(),
-                "tick-low", useLowTickDefault ? 1 : 0) == 0) {
-            primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
-        }
-        float tickIntensity = Settings.Global.getFloat(
-                context.getContentResolver(),
-                "tick-intensity",
-                context.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
-        int tickDelay = Settings.Global.getInt(
-                context.getContentResolver(),
-                "tick-delay",
-                context.getResources().getInteger(R.integer.config_udfpsTickDelay));
-
-        VibrationEffect.Composition composition = VibrationEffect.startComposition();
-        composition.addPrimitive(primitiveTick, tickIntensity, 0);
-        int primitives = (int) (duration / tickDelay);
-        float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
-        if (useShortRampUp) {
-            rampUp = new float[]{.5f, .7f};
-        }
-        for (int i = 0; i < rampUp.length; i++) {
-            composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay);
-        }
-        for (int i = rampUp.length; i < primitives; i++) {
-            composition.addPrimitive(primitiveTick, tickIntensity, tickDelay);
-        }
-        return composition.compose();
-    }
-
-    /**
      * Play haptic to signal udfps scanning started.
      */
     @VisibleForTesting
@@ -670,8 +625,8 @@
             mVibrator.vibrate(
                     Process.myUid(),
                     mContext.getOpPackageName(),
-                    mTick,
-                    "udfps-onStart-tick",
+                    EFFECT_CLICK,
+                    "udfps-onStart-click",
                     VIBRATION_ATTRIBUTES);
         }
     }
@@ -863,6 +818,7 @@
                         mServerRequest.mEnrollHelper,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
@@ -881,6 +837,7 @@
                         mSystemClock,
                         mKeyguardStateController,
                         mUnlockedScreenOffAnimationController,
+                        mDialogManager,
                         this
                 );
             case BiometricOverlayConstants.REASON_AUTH_BP:
@@ -891,6 +848,7 @@
                         bpView,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             case BiometricOverlayConstants.REASON_AUTH_OTHER:
@@ -902,6 +860,7 @@
                         authOtherView,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             default:
@@ -1058,7 +1017,6 @@
             }
         }
         mOnFingerDown = false;
-        mVibrator.cancel();
         if (mView.isIlluminationRequested()) {
             mView.stopIllumination();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1f01fc5..ac38b13 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,9 +20,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -30,7 +28,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.TypedValue;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
@@ -38,7 +35,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.graphics.ColorUtils;
 import com.android.systemui.R;
 
 /**
@@ -106,9 +102,8 @@
 
         mSensorOutlinePaint = new Paint(0 /* flags */);
         mSensorOutlinePaint.setAntiAlias(true);
-        mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
-        mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
-        mSensorOutlinePaint.setStrokeWidth(2.f);
+        mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill));
+        mSensorOutlinePaint.setStyle(Paint.Style.FILL);
 
         mBlueFill = new Paint(0 /* flags */);
         mBlueFill.setAntiAlias(true);
@@ -117,12 +112,12 @@
 
         mMovingTargetFpIcon = context.getResources()
                 .getDrawable(R.drawable.ic_kg_fingerprint, null);
-        mMovingTargetFpIcon.setTint(Color.WHITE);
+        mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon));
         mMovingTargetFpIcon.mutate();
 
         mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
 
-        mHintColorFaded = getHintColorFaded(context);
+        mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
         mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
         mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
         mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
@@ -218,22 +213,6 @@
         };
     }
 
-    @ColorInt
-    private static int getHintColorFaded(@NonNull Context context) {
-        final TypedValue tv = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
-        final int alpha = (int) (tv.getFloat() * 255f);
-
-        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
-        final TypedArray ta = context.obtainStyledAttributes(attrs);
-        try {
-            @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled));
-            return ColorUtils.setAlphaComponent(color, alpha);
-        } finally {
-            ta.recycle();
-        }
-    }
-
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
         mEnrollHelper = helper;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 79c7e66..631a461 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -18,12 +18,10 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 
@@ -80,24 +78,11 @@
 
         mBackgroundPaint = new Paint();
         mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
-        mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+        mBackgroundPaint.setColor(context.getColor(R.color.udfps_moving_target_fill));
         mBackgroundPaint.setAntiAlias(true);
         mBackgroundPaint.setStyle(Paint.Style.STROKE);
         mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
 
-        // Set background paint color and alpha.
-        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
-        final TypedArray typedArray = context.obtainStyledAttributes(attrs);
-        try {
-            @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor());
-            mBackgroundPaint.setColor(tintColor);
-        } finally {
-            typedArray.recycle();
-        }
-        TypedValue alpha = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
-        mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
-
         // Progress fill should *not* use the extracted system color.
         mFillPaint = new Paint();
         mFillPaint.setStrokeWidth(mStrokeWidthPx);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 292a904..ac9e92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -22,6 +22,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -54,8 +55,10 @@
             @NonNull UdfpsEnrollHelper enrollHelper,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
         mEnrollProgressBarRadius = getContext().getResources()
                 .getInteger(R.integer.config_udfpsEnrollProgressBar);
         mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6198733..97dca0f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -33,8 +34,10 @@
             @NonNull UdfpsFpmOtherView view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8f4d6f6..3e8a568 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -86,8 +87,10 @@
             @NonNull SystemClock systemClock,
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull UdfpsController udfpsController) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockScreenShadeTransitionController = transitionController;
@@ -217,6 +220,10 @@
             return false;
         }
 
+        if (mDialogManager.shouldHideAffordance()) {
+            return true;
+        }
+
         if (mLaunchTransitionFadingAway) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
index 53586f5..42ecd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
@@ -35,7 +35,23 @@
      * {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
      */
     interface Connector {
-        ListenableFuture<Optional<CommunalSource>> connect();
+        Connection connect(Connection.Callback callback);
+    }
+
+    /**
+     * {@link Connection} defines an interface for an entity which holds the necessary components
+     * for establishing and maintaining a connection to the communal source.
+     */
+    interface Connection {
+        /**
+         * {@link Callback} defines an interface for clients to be notified when a source is ready
+         */
+        interface Callback {
+            void onSourceEstablished(Optional<CommunalSource> source);
+            void onDisconnected();
+        }
+
+        void disconnect();
     }
 
     /**
@@ -86,29 +102,4 @@
      * value will be {@code null} in case of a failure.
      */
     ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
-
-    /**
-     * Adds a {@link Callback} to receive future status updates regarding this
-     * {@link CommunalSource}.
-     *
-     * @param callback The {@link Callback} to be added.
-     */
-    void addCallback(Callback callback);
-
-    /**
-     * Removes a {@link Callback} from receiving future updates.
-     *
-     * @param callback The {@link Callback} to be removed.
-     */
-    void removeCallback(Callback callback);
-
-    /**
-     * An interface for receiving updates on the state of the {@link CommunalSource}.
-     */
-    interface Callback {
-        /**
-         * Invoked when the {@link CommunalSource} is no longer available for use.
-         */
-        void onDisconnected();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index d3018e3..4aa46f18 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -21,12 +21,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 
 import com.google.android.collect.Lists;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -41,6 +43,7 @@
     // A list of {@link Callback} that have registered to receive updates.
     private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
     private final CommunalConditionsMonitor mConditionsMonitor;
+    private final Executor mExecutor;
 
     private CommunalSource mCurrentSource;
 
@@ -50,14 +53,6 @@
     // Whether the class is currently listening for condition changes.
     private boolean mListeningForConditions = false;
 
-    private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
-        @Override
-        public void onDisconnected() {
-            // Clear source reference.
-            setSource(null /* source */);
-        }
-    };
-
     private final CommunalConditionsMonitor.Callback mConditionsCallback =
             allConditionsMet -> {
                 if (mAllCommunalConditionsMet != allConditionsMet) {
@@ -70,7 +65,9 @@
 
     @VisibleForTesting
     @Inject
-    public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
+    public CommunalSourceMonitor(@Main Executor executor,
+            CommunalConditionsMonitor communalConditionsMonitor) {
+        mExecutor = executor;
         mConditionsMonitor = communalConditionsMonitor;
     }
 
@@ -81,35 +78,28 @@
      * @param source The new {@link CommunalSource}.
      */
     public void setSource(CommunalSource source) {
-        if (mCurrentSource != null) {
-            mCurrentSource.removeCallback(mSourceCallback);
-        }
-
         mCurrentSource = source;
 
         if (mAllCommunalConditionsMet) {
             executeOnSourceAvailableCallbacks();
         }
-
-        // Add callback to be informed when the source disconnects.
-        if (mCurrentSource != null) {
-            mCurrentSource.addCallback(mSourceCallback);
-        }
     }
 
     private void executeOnSourceAvailableCallbacks() {
-        // If the new source is valid, inform registered Callbacks of its presence.
-        Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
-        while (itr.hasNext()) {
-            Callback cb = itr.next().get();
-            if (cb == null) {
-                itr.remove();
-            } else {
-                cb.onSourceAvailable(
-                        (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
-                                mCurrentSource) : null);
+        mExecutor.execute(() -> {
+            // If the new source is valid, inform registered Callbacks of its presence.
+            Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
+            while (itr.hasNext()) {
+                Callback cb = itr.next().get();
+                if (cb == null) {
+                    itr.remove();
+                } else {
+                    cb.onSourceAvailable(
+                            (mAllCommunalConditionsMet && mCurrentSource != null)
+                                    ? new WeakReference<>(mCurrentSource) : null);
+                }
             }
-        }
+        });
     }
 
     /**
@@ -118,17 +108,19 @@
      * @param callback The {@link Callback} to add.
      */
     public void addCallback(Callback callback) {
-        mCallbacks.add(new WeakReference<>(callback));
+        mExecutor.execute(() -> {
+            mCallbacks.add(new WeakReference<>(callback));
 
-        // Inform the callback of any already present CommunalSource.
-        if (mAllCommunalConditionsMet && mCurrentSource != null) {
-            callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
-        }
+            // Inform the callback of any already present CommunalSource.
+            if (mAllCommunalConditionsMet && mCurrentSource != null) {
+                callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
+            }
 
-        if (!mListeningForConditions) {
-            mConditionsMonitor.addCallback(mConditionsCallback);
-            mListeningForConditions = true;
-        }
+            if (!mListeningForConditions) {
+                mConditionsMonitor.addCallback(mConditionsCallback);
+                mListeningForConditions = true;
+            }
+        });
     }
 
     /**
@@ -137,12 +129,14 @@
      * @param callback The {@link Callback} to add.
      */
     public void removeCallback(Callback callback) {
-        mCallbacks.removeIf(el -> el.get() == callback);
+        mExecutor.execute(() -> {
+            mCallbacks.removeIf(el -> el.get() == callback);
 
-        if (mCallbacks.isEmpty() && mListeningForConditions) {
-            mConditionsMonitor.removeCallback(mConditionsCallback);
-            mListeningForConditions = false;
-        }
+            if (mCallbacks.isEmpty() && mListeningForConditions) {
+                mConditionsMonitor.removeCallback(mConditionsCallback);
+                mListeningForConditions = false;
+            }
+        });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
index 1044239..13b1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -27,8 +27,6 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
-import com.google.common.util.concurrent.ListenableFuture;
-
 import java.util.Optional;
 
 import javax.inject.Inject;
@@ -53,10 +51,11 @@
 
     private int mReconnectAttempts = 0;
     private Runnable mCurrentReconnectCancelable;
-    private ListenableFuture<Optional<CommunalSource>> mGetSourceFuture;
 
-    private final Optional<CommunalSource.Connector> mConnector;
     private final Optional<CommunalSource.Observer> mObserver;
+    private final Optional<CommunalSource.Connector> mConnector;
+
+    private CommunalSource.Connection mCurrentConnection;
 
     private final Runnable mConnectRunnable = new Runnable() {
         @Override
@@ -66,6 +65,10 @@
         }
     };
 
+    private final CommunalSource.Observer.Callback mObserverCallback = () -> {
+        initiateConnectionAttempt();
+    };
+
     @Inject
     public CommunalSourcePrimer(Context context, @Main Resources resources,
             SystemClock clock,
@@ -132,7 +135,7 @@
     @Override
     protected void onBootCompleted() {
         if (mObserver.isPresent()) {
-            mObserver.get().addCallback(() -> initiateConnectionAttempt());
+            mObserver.get().addCallback(mObserverCallback);
         }
         initiateConnectionAttempt();
     }
@@ -142,34 +145,36 @@
             Log.d(TAG, "attempting to communal to communal source");
         }
 
-        if (mGetSourceFuture != null) {
+        if (mCurrentConnection != null) {
             if (DEBUG) {
                 Log.d(TAG, "canceling in-flight connection");
             }
-            mGetSourceFuture.cancel(true);
+            mCurrentConnection.disconnect();
         }
 
-        mGetSourceFuture = mConnector.get().connect();
-        mGetSourceFuture.addListener(() -> {
-            try {
-                final long startTime = mSystemClock.currentTimeMillis();
-                Optional<CommunalSource> result = mGetSourceFuture.get();
-                if (result.isPresent()) {
-                    final CommunalSource source = result.get();
-                    source.addCallback(() -> {
-                        if (mSystemClock.currentTimeMillis() - startTime > mMinConnectionDuration) {
-                            initiateConnectionAttempt();
-                        } else {
-                            scheduleConnectionAttempt();
-                        }
-                    });
+        mCurrentConnection = mConnector.get().connect(new CommunalSource.Connection.Callback() {
+            private long mStartTime;
+
+            @Override
+            public void onSourceEstablished(Optional<CommunalSource> optionalSource) {
+                mStartTime = mSystemClock.currentTimeMillis();
+
+                if (optionalSource.isPresent()) {
+                    final CommunalSource source = optionalSource.get();
                     mMonitor.setSource(source);
                 } else {
                     scheduleConnectionAttempt();
                 }
-            } catch (Exception e) {
-                e.printStackTrace();
             }
-        }, mMainExecutor);
+
+            @Override
+            public void onDisconnected() {
+                if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
+                    initiateConnectionAttempt();
+                } else {
+                    scheduleConnectionAttempt();
+                }
+            }
+        });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 38e4d78..e337678 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -24,6 +24,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.media.taptotransfer.MediaTttChipController;
+import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.people.PeopleProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -32,6 +33,7 @@
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -39,7 +41,6 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.startingsurface.StartingSurface;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -111,7 +112,7 @@
         Builder setRecentTasks(Optional<RecentTasks> r);
 
         @BindsInstance
-        Builder setSizeCompatUI(Optional<SizeCompatUI> s);
+        Builder setCompatUI(Optional<CompatUI> s);
 
         @BindsInstance
         Builder setDragAndDrop(Optional<DragAndDrop> d);
@@ -133,6 +134,7 @@
         getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
         // No init method needed, just needs to be gotten so that it's created.
         getMediaTttChipController();
+        getMediaTttCommandLineHelper();
     }
 
     /**
@@ -182,6 +184,9 @@
     /** */
     Optional<MediaTttChipController> getMediaTttChipController();
 
+    /** */
+    Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper();
+
     /**
      * Member injection into the supplied argument.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 90a3ad2..b815d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
 import com.android.wm.shell.dagger.WMShellModule;
 import com.android.wm.shell.dagger.WMSingleton;
@@ -35,7 +36,6 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.startingsurface.StartingSurface;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -119,7 +119,7 @@
     Optional<RecentTasks> getRecentTasks();
 
     @WMSingleton
-    SizeCompatUI getSizeCompatUI();
+    CompatUI getCompatUI();
 
     @WMSingleton
     DragAndDrop getDragAndDrop();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ff14064..2ebcd853 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -121,6 +121,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -236,6 +237,7 @@
     protected Handler mMainHandler;
     private int mSmallestScreenWidthDp;
     private final Optional<StatusBar> mStatusBarOptional;
+    private final SystemUIDialogManager mDialogManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
 
@@ -346,7 +348,8 @@
             PackageManager packageManager,
             Optional<StatusBar> statusBarOptional,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            DialogLaunchAnimator dialogLaunchAnimator) {
+            DialogLaunchAnimator dialogLaunchAnimator,
+            SystemUIDialogManager dialogManager) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -378,6 +381,7 @@
         mStatusBarOptional = statusBarOptional;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mDialogManager = dialogManager;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -677,7 +681,8 @@
                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
                 mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
-                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
+                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
+                mDialogManager);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -2219,10 +2224,12 @@
                 SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
                 Optional<StatusBar> statusBarOptional,
-                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
+                SystemUIDialogManager systemUiDialogManager) {
             // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
             // dismiss this dialog when the device is locked.
-            super(context, themeRes, false /* dismissOnDeviceLock */);
+            super(context, themeRes, false /* dismissOnDeviceLock */,
+                    systemUiDialogManager);
             mContext = context;
             mAdapter = adapter;
             mOverflowAdapter = overflowAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c14d32e..c592431 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2099,6 +2099,15 @@
     private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
         @Override
         public void run() {
+            // If the keyguard is already going away, or it's about to because we are going to
+            // trigger the going-away remote animation to show the surface behind, don't do it
+            // again. That will cause the current animation to be cancelled unnecessarily.
+            if (mKeyguardStateController.isKeyguardGoingAway()
+                    || mSurfaceBehindRemoteAnimationRequested
+                    || mSurfaceBehindRemoteAnimationRunning) {
+                return;
+            }
+
             Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
             if (DEBUG) Log.d(TAG, "keyguardGoingAway");
             mKeyguardViewControllerLazy.get().keyguardGoingAway();
@@ -2457,9 +2466,7 @@
 
         if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
             try {
-                if (!cancelled) {
-                    mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
-                }
+                mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
                 mSurfaceBehindRemoteAnimationFinishedCallback = null;
             } catch (RemoteException e) {
                 e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 49a63c3..d926e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -728,9 +728,9 @@
                     PlaybackState.ACTION_SKIP_TO_NEXT)
 
             // Then, check for custom actions
-            val customActions = MutableList<MediaAction?>(4) { null }
+            val customActions = MutableList<MediaAction?>(MAX_CUSTOM_ACTIONS) { null }
             var customCount = 0
-            for (i in 0..MAX_CUSTOM_ACTIONS) {
+            for (i in 0..(MAX_CUSTOM_ACTIONS - 1)) {
                 getCustomAction(state, packageName, controller, customCount)?.let {
                     customActions[customCount++] = it
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index cf679f0..a1f8455 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -25,6 +25,7 @@
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.media.MediaHostStatesManager;
 import com.android.systemui.media.taptotransfer.MediaTttChipController;
+import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 
@@ -78,11 +79,23 @@
     static Optional<MediaTttChipController> providesMediaTttChipController(
             MediaTttFlags mediaTttFlags,
             Context context,
-            CommandRegistry commandRegistry,
             WindowManager windowManager) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipController(commandRegistry, context, windowManager));
+        return Optional.of(new MediaTttChipController(context, windowManager));
+    }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
+            MediaTttFlags mediaTttFlags,
+            CommandRegistry commandRegistry,
+            MediaTttChipController mediaTttChipController) {
+        if (!mediaTttFlags.isMediaTttEnabled()) {
+            return Optional.empty();
+        }
+        return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 7f5744c..a9e9f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -45,6 +45,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Base dialog for media output UI
@@ -53,6 +54,7 @@
         MediaOutputController.Callback, Window.Callback {
 
     private static final String TAG = "MediaOutputDialog";
+    private static final String EMPTY_TITLE = " ";
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
     private final RecyclerView.LayoutManager mLayoutManager;
@@ -83,8 +85,9 @@
         }
     };
 
-    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
-        super(context, R.style.Theme_SystemUI_Dialog_Media);
+    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
+            SystemUIDialogManager dialogManager) {
+        super(context, R.style.Theme_SystemUI_Dialog_Media, dialogManager);
 
         // Save the context that is wrapped with our theme.
         mContext = getContext();
@@ -108,6 +111,9 @@
         lp.setFitInsetsIgnoringVisibility(true);
         window.setAttributes(lp);
         window.setContentView(mDialogView);
+        // Sets window to a blank string to avoid talkback announce app label first when pop up,
+        // which doesn't make sense.
+        window.setTitle(EMPTY_TITLE);
 
         mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
         mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 56317c6..4eee60c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -61,6 +61,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -85,6 +86,7 @@
     private final ShadeController mShadeController;
     private final ActivityStarter mActivityStarter;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
+    private final SystemUIDialogManager mDialogManager;
     private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
     private final boolean mAboveStatusbar;
     private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -106,7 +108,7 @@
             boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
             lbm, ShadeController shadeController, ActivityStarter starter,
             NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
-            DialogLaunchAnimator dialogLaunchAnimator) {
+            DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
         mContext = context;
         mPackageName = packageName;
         mMediaSessionManager = mediaSessionManager;
@@ -122,6 +124,7 @@
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
+        mDialogManager = dialogManager;
     }
 
     void start(@NonNull Callback cb) {
@@ -512,9 +515,10 @@
         // We show the output group dialog from the output dialog.
         MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
                 mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
-                mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+                mDialogManager);
         MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
-                controller);
+                controller, mDialogManager);
         mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 7696a1f..4e9da55 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -29,6 +29,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output transferring.
@@ -38,8 +39,9 @@
     final UiEventLogger mUiEventLogger;
 
     MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController, UiEventLogger uiEventLogger) {
-        super(context, mediaOutputController);
+            mediaOutputController, UiEventLogger uiEventLogger,
+            SystemUIDialogManager dialogManager) {
+        super(context, mediaOutputController, dialogManager);
         mUiEventLogger = uiEventLogger;
         mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index b91901d..a7bc852 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import javax.inject.Inject
 
 /**
@@ -38,7 +39,8 @@
     private val starter: ActivityStarter,
     private val notificationEntryManager: NotificationEntryManager,
     private val uiEventLogger: UiEventLogger,
-    private val dialogLaunchAnimator: DialogLaunchAnimator
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val dialogManager: SystemUIDialogManager
 ) {
     companion object {
         var mediaOutputDialog: MediaOutputDialog? = null
@@ -51,8 +53,9 @@
 
         val controller = MediaOutputController(context, packageName, aboveStatusBar,
             mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
-            uiEventLogger, dialogLaunchAnimator)
-        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+            uiEventLogger, dialogLaunchAnimator, dialogManager)
+        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
+                dialogManager)
         mediaOutputDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index f1c6601..9f752b9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -25,6 +25,7 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output group.
@@ -33,8 +34,8 @@
 public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
 
     MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController) {
-        super(context, mediaOutputController);
+            mediaOutputController, SystemUIDialogManager dialogManager) {
+        super(context, mediaOutputController, dialogManager);
         mMediaOutputController.resetGroupMediaDevices();
         mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index af8fc0e..1ec6a39 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -24,13 +24,8 @@
 import android.view.WindowManager
 import android.widget.LinearLayout
 import android.widget.TextView
-import androidx.annotation.StringRes
-import androidx.annotation.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import java.io.PrintWriter
 import javax.inject.Inject
 
 /**
@@ -41,14 +36,9 @@
  */
 @SysUISingleton
 class MediaTttChipController @Inject constructor(
-    commandRegistry: CommandRegistry,
     private val context: Context,
     private val windowManager: WindowManager,
 ) {
-    init {
-        commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
-        commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
-    }
 
     private val windowLayoutParams = WindowManager.LayoutParams().apply {
         width = WindowManager.LayoutParams.WRAP_CONTENT
@@ -65,7 +55,8 @@
     /** The chip view currently being displayed. Null if the chip is not being displayed. */
     private var chipView: LinearLayout? = null
 
-    private fun displayChip(chipType: ChipType, otherDeviceName: String) {
+    /** Displays the chip view for the given state. */
+    fun displayChip(chipState: MediaTttChipState) {
         val oldChipView = chipView
         if (chipView == null) {
             chipView = LayoutInflater
@@ -76,16 +67,16 @@
 
         // Text
         currentChipView.requireViewById<TextView>(R.id.text).apply {
-            text = context.getString(chipType.chipText, otherDeviceName)
+            text = context.getString(chipState.chipText, chipState.otherDeviceName)
         }
 
         // Loading
-        val showLoading = chipType == ChipType.TRANSFER_INITIATED
+        val showLoading = chipState is TransferInitiated
         currentChipView.requireViewById<View>(R.id.loading).visibility =
             if (showLoading) { View.VISIBLE } else { View.GONE }
 
         // Undo
-        val showUndo = chipType == ChipType.TRANSFER_SUCCEEDED
+        val showUndo = chipState is TransferSucceeded
         currentChipView.requireViewById<View>(R.id.undo).visibility =
             if (showUndo) { View.VISIBLE } else { View.GONE }
 
@@ -94,53 +85,10 @@
         }
     }
 
-    private fun removeChip() {
+    /** Hides the chip. */
+    fun removeChip() {
         if (chipView == null) { return }
         windowManager.removeView(chipView)
         chipView = null
     }
-
-    @VisibleForTesting
-    enum class ChipType(
-        @StringRes internal val chipText: Int
-    ) {
-        MOVE_CLOSER_TO_TRANSFER(R.string.media_move_closer_to_transfer),
-        TRANSFER_INITIATED(R.string.media_transfer_playing),
-        TRANSFER_SUCCEEDED(R.string.media_transfer_playing),
-    }
-
-    inner class AddChipCommand : Command {
-        override fun execute(pw: PrintWriter, args: List<String>) {
-            val chipTypeArg = args[1]
-            ChipType.values().forEach {
-                if (it.name == chipTypeArg) {
-                    displayChip(it, otherDeviceName = args[0])
-                    return
-                }
-            }
-
-            pw.println("Chip type must be one of " +
-                    ChipType.values().map { it.name }.reduce { acc, s -> "$acc, $s" })
-        }
-
-        override fun help(pw: PrintWriter) {
-            pw.println(
-                "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG <deviceName> <chipType>"
-            )
-        }
-    }
-
-    inner class RemoveChipCommand : Command {
-        override fun execute(pw: PrintWriter, args: List<String>) = removeChip()
-        override fun help(pw: PrintWriter) {
-            pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
-        }
-    }
-
-    companion object {
-        @VisibleForTesting
-        const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
-        @VisibleForTesting
-        const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
new file mode 100644
index 0000000..05b9552
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.media.taptotransfer
+
+import androidx.annotation.StringRes
+import com.android.systemui.R
+
+/**
+ * A class that stores all the information necessary to display the media tap-to-transfer chip in
+ * certain states.
+ *
+ * This is a sealed class where each subclass represents a specific chip state. Each subclass can
+ * contain additional information that is necessary for only that state.
+ */
+sealed class MediaTttChipState(
+    /** A string resource for the text that the chip should display. */
+    @StringRes internal val chipText: Int,
+    /** The name of the other device involved in the transfer. */
+    internal val otherDeviceName: String
+)
+
+/**
+ * A state representing that the two devices are close but not close enough to initiate a transfer.
+ * The chip will instruct the user to move closer in order to initiate the transfer.
+ */
+class MoveCloserToTransfer(
+    otherDeviceName: String
+) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName)
+
+/**
+ * A state representing that a transfer has been initiated (but not completed).
+ */
+class TransferInitiated(
+    otherDeviceName: String
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
+
+/**
+ * A state representing that a transfer has been successfully completed.
+ */
+class TransferSucceeded(
+    otherDeviceName: String
+) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
new file mode 100644
index 0000000..9f05c92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.media.taptotransfer
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A helper class to test the media tap-to-transfer chip via the command line. See inner classes for
+ * command usages.
+ */
+@SysUISingleton
+class MediaTttCommandLineHelper @Inject constructor(
+    commandRegistry: CommandRegistry,
+    private val mediaTttChipController: MediaTttChipController
+) {
+    init {
+        commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
+        commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() }
+    }
+
+    inner class AddChipCommand : Command {
+        override fun execute(pw: PrintWriter, args: List<String>) {
+            val otherDeviceName = args[0]
+            when (args[1]) {
+                MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
+                    mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName))
+                }
+                TRANSFER_INITIATED_COMMAND_NAME -> {
+                    mediaTttChipController.displayChip(TransferInitiated(otherDeviceName))
+                }
+                TRANSFER_SUCCEEDED_COMMAND_NAME -> {
+                    mediaTttChipController.displayChip(TransferSucceeded(otherDeviceName))
+                }
+                else -> {
+                    pw.println("Chip type must be one of " +
+                            "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " +
+                            "$TRANSFER_INITIATED_COMMAND_NAME, " +
+                            TRANSFER_SUCCEEDED_COMMAND_NAME
+                    )
+                }
+            }
+        }
+
+        override fun help(pw: PrintWriter) {
+            pw.println(
+                "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG <deviceName> <chipStatus>"
+            )
+        }
+    }
+
+    inner class RemoveChipCommand : Command {
+        override fun execute(pw: PrintWriter, args: List<String>) {
+            mediaTttChipController.removeChip()
+        }
+        override fun help(pw: PrintWriter) {
+            pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
+        }
+    }
+}
+
+@VisibleForTesting
+const val ADD_CHIP_COMMAND_TAG = "media-ttt-chip-add"
+@VisibleForTesting
+const val REMOVE_CHIP_COMMAND_TAG = "media-ttt-chip-remove"
+@VisibleForTesting
+val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 8026df7..cc91384 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1251,6 +1251,7 @@
     private void onImeSwitcherClick(View v) {
         mInputMethodManager.showInputMethodPickerFromSystem(
                 true /* showAuxiliarySubtypes */, mDisplayId);
+        mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
     };
 
     private boolean onLongPressBackHome(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index debd2eb..d27b716 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -96,6 +96,9 @@
         @UiEvent(doc = "The overview button was pressed in the navigation bar.")
         NAVBAR_OVERVIEW_BUTTON_TAP(535),
 
+        @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.")
+        NAVBAR_IME_SWITCHER_BUTTON_TAP(923),
+
         @UiEvent(doc = "The home button was long-pressed in the navigation bar.")
         NAVBAR_HOME_BUTTON_LONGPRESS(536),
 
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 b904505..9acd3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -56,6 +56,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 import javax.inject.Inject;
 
@@ -364,11 +365,13 @@
         }
         info.state.expandedAccessibilityClassName = "";
 
-        // The holder has a tileView, therefore this call is not null
-        holder.getTileAsCustomizeView().changeState(info.state);
-        holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
+        CustomizeTileView tileView =
+                Objects.requireNonNull(
+                        holder.getTileAsCustomizeView(), "The holder must have a tileView");
+        tileView.changeState(info.state);
+        tileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
         // Don't show the side view for third party tiles, as we don't have the actual state.
-        holder.getTileAsCustomizeView().setShowSideView(position < mEditIndex || info.isSystem);
+        tileView.setShowSideView(position < mEditIndex || info.isSystem);
         holder.mTileView.setSelected(true);
         holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
         holder.mTileView.setClickable(true);
@@ -448,7 +451,12 @@
         mFocusIndex = mEditIndex - 1;
         mNeedsFocus = true;
         if (mRecyclerView != null) {
-            mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex));
+            mRecyclerView.post(() -> {
+                final RecyclerView recyclerView = mRecyclerView;
+                if (recyclerView != null) {
+                    recyclerView.smoothScrollToPosition(mFocusIndex);
+                }
+            });
         }
         notifyDataSetChanged();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 0f2db33..bfa2aaa4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -46,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * Runs the day-to-day operations of which tiles should be bound and when.
@@ -178,6 +179,13 @@
                 return;
             }
             TileServiceManager service = mServices.get(customTile);
+            if (service == null) {
+                Log.e(
+                        TAG,
+                        "No TileServiceManager found in requestListening for tile "
+                                + customTile.getTileSpec());
+                return;
+            }
             if (!service.isActiveTile()) {
                 return;
             }
@@ -236,7 +244,7 @@
             verifyCaller(customTile);
             customTile.onDialogShown();
             mHost.forceCollapsePanels();
-            mServices.get(customTile).setShowingDialog(true);
+            Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(true);
         }
     }
 
@@ -245,7 +253,7 @@
         CustomTile customTile = getTileForToken(token);
         if (customTile != null) {
             verifyCaller(customTile);
-            mServices.get(customTile).setShowingDialog(false);
+            Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(false);
             customTile.onDialogHidden();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index e3f085c..2a39849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -303,15 +303,11 @@
         if (DEBUG) {
             Log.d(TAG, "updateDialog");
         }
-        if (mInternetDialogController.isAirplaneModeEnabled()) {
-            mInternetDialogSubTitle.setVisibility(View.GONE);
-            mAirplaneModeLayout.setVisibility(View.VISIBLE);
-        } else {
-            mInternetDialogTitle.setText(getDialogTitleText());
-            mInternetDialogSubTitle.setVisibility(View.VISIBLE);
-            mInternetDialogSubTitle.setText(getSubtitleText());
-            mAirplaneModeLayout.setVisibility(View.GONE);
-        }
+        mInternetDialogTitle.setText(getDialogTitleText());
+        mInternetDialogSubTitle.setText(getSubtitleText());
+        mAirplaneModeLayout.setVisibility(
+                mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+
         updateEthernet();
         if (shouldUpdateMobileNetwork) {
             setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 7dd24b4..4908d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -878,10 +878,8 @@
         if (accessPoints == null || accessPoints.size() == 0) {
             mConnectedEntry = null;
             mWifiEntriesCount = 0;
-            if (mCallback != null) {
-                mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */,
-                        false /* hasMoreEntry */);
-            }
+            mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */,
+                    false /* hasMoreEntry */);
             return;
         }
 
@@ -913,9 +911,7 @@
         mConnectedEntry = connectedEntry;
         mWifiEntriesCount = wifiEntries.size();
 
-        if (mCallback != null) {
-            mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry);
-        }
+        mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3ed7e84..e7cd1e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -76,6 +76,7 @@
 
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.Dumpable;
@@ -88,6 +89,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -161,6 +163,7 @@
     private final Optional<StartingSurface> mStartingSurface;
     private final SmartspaceTransitionController mSmartspaceTransitionController;
     private final Optional<RecentTasks> mRecentTasks;
+    private final UiEventLogger mUiEventLogger;
 
     private Region mActiveNavBarRegion;
 
@@ -248,6 +251,7 @@
             mContext.getSystemService(InputMethodManager.class)
                     .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
                             DEFAULT_DISPLAY);
+            mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
         }
 
         @Override
@@ -560,6 +564,7 @@
             ShellTransitions shellTransitions,
             ScreenLifecycle screenLifecycle,
             SmartspaceTransitionController smartspaceTransitionController,
+            UiEventLogger uiEventLogger,
             DumpManager dumpManager) {
         super(broadcastDispatcher);
         mContext = context;
@@ -581,6 +586,7 @@
         mOneHandedOptional = oneHandedOptional;
         mShellTransitions = shellTransitions;
         mRecentTasks = recentTasks;
+        mUiEventLogger = uiEventLogger;
 
         // Assumes device always starts with back button until launcher tells it that it does not
         mNavBarButtonAlpha = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 85bf98c..7f130cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -248,8 +249,8 @@
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
             WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
-            if (!QuickStepContract.isGesturalMode(mNavBarMode) 
-            	    && wm.hasSoftNavigationBar(mContext.getDisplayId())) {
+            if (!QuickStepContract.isGesturalMode(mNavBarMode)
+            	    && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f43d9c3..d7b4738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -887,7 +887,13 @@
                 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
             }
         } else {
-            showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+            if (mKeyguardUpdateMonitor.isUdfpsSupported()
+                    && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+                    KeyguardUpdateMonitor.getCurrentUser())) {
+                showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press));
+            } else {
+                showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
new file mode 100644
index 0000000..a1d086b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -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.systemui.statusbar.charging
+
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.RuntimeShader
+import android.util.MathUtils
+
+/**
+ * Shader class that renders a distorted ripple for the UDFPS dwell effect.
+ * Adjustable shader parameters:
+ *   - progress
+ *   - origin
+ *   - color
+ *   - time
+ *   - maxRadius
+ *   - distortionStrength.
+ * See per field documentation for more details.
+ *
+ * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
+ */
+class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
+    companion object {
+        private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+                uniform float in_time;
+                uniform float in_radius;
+                uniform float in_blur;
+                uniform vec4 in_color;
+                uniform float in_phase1;
+                uniform float in_phase2;
+                uniform float in_distortion_strength;"""
+        private const val SHADER_LIB = """
+                float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
+                  float blurHalf = blur * 0.5;
+                  float d = distance(uv, xy);
+                  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
+                }
+
+                float softRing(vec2 uv, vec2 xy, float radius, float blur) {
+                  float thickness_half = radius * 0.25;
+                  float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
+                  float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
+                  return circle_outer - circle_inner;
+                }
+
+                vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
+                    return p + vec2(sin(p.x * frequency + in_phase1),
+                                    cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy;
+                }
+
+                vec4 ripple(vec2 p, float distort_xy, float frequency) {
+                    vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
+                    float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
+                    float rippleAlpha = max(circle,
+                        softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
+                    return in_color * rippleAlpha;
+                }
+                """
+        private const val SHADER_MAIN = """vec4 main(vec2 p) {
+                    vec4 color1 = ripple(p,
+                        12 * in_distortion_strength, // distort_xy
+                        0.012 // frequency
+                    );
+                    vec4 color2 = ripple(p,
+                        17.5 * in_distortion_strength, // distort_xy
+                        0.018 // frequency
+                    );
+                    // Alpha blend between two layers.
+                    return vec4(color1.xyz + color2.xyz
+                        * (1 - color1.w), color1.w + color2.w * (1-color1.w));
+                }"""
+        private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
+    }
+
+    /**
+     * Maximum radius of the ripple.
+     */
+    var maxRadius: Float = 0.0f
+
+    /**
+     * Origin coordinate of the ripple.
+     */
+    var origin: PointF = PointF()
+        set(value) {
+            field = value
+            setUniform("in_origin", floatArrayOf(value.x, value.y))
+        }
+
+    /**
+     * Progress of the ripple. Float value between [0, 1].
+     */
+    var progress: Float = 0.0f
+        set(value) {
+            field = value
+            setUniform("in_radius",
+                    (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
+            setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
+        }
+
+    /**
+     * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
+     */
+    var distortionStrength: Float = 0.0f
+        set(value) {
+            field = value
+            setUniform("in_distortion_strength", value)
+        }
+
+    /**
+     * Play time since the start of the effect in seconds.
+     */
+    var time: Float = 0.0f
+        set(value) {
+            field = value * 0.001f
+            setUniform("in_time", field)
+            setUniform("in_phase1", field * 2f + 0.367f)
+            setUniform("in_phase2", field * 5.2f * 1.531f)
+        }
+
+    /**
+     * A hex value representing the ripple color, in the format of ARGB
+     */
+    var color: Int = 0xffffff.toInt()
+        set(value) {
+            field = value
+            val color = Color.valueOf(value)
+            setUniform("in_color", floatArrayOf(color.red(),
+                    color.green(), color.blue(), color.alpha()))
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index ce86953..962c7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -102,12 +102,17 @@
 
         configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
             override fun onLayoutDirectionChanged(isRtl: Boolean) {
-                synchronized(this) {
-                    val corner = selectDesignatedCorner(nextViewState.rotation, isRtl)
-                    nextViewState = nextViewState.copy(
-                            layoutRtl = isRtl,
-                            designatedCorner = corner
-                    )
+                uiExecutor?.execute {
+                    // If rtl changed, hide all dotes until the next state resolves
+                    setCornerVisibilities(View.INVISIBLE)
+
+                    synchronized(this) {
+                        val corner = selectDesignatedCorner(nextViewState.rotation, isRtl)
+                        nextViewState = nextViewState.copy(
+                                layoutRtl = isRtl,
+                                designatedCorner = corner
+                        )
+                    }
                 }
             }
         })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 1940cb2..54f1380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
 
 import javax.inject.Inject;
 
@@ -44,6 +45,7 @@
 @SysUISingleton
 public class NotificationFilter {
 
+    private final DebugModeFilterProvider mDebugNotificationFilter;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardEnvironment mKeyguardEnvironment;
     private final ForegroundServiceController mForegroundServiceController;
@@ -52,11 +54,13 @@
 
     @Inject
     public NotificationFilter(
+            DebugModeFilterProvider debugNotificationFilter,
             StatusBarStateController statusBarStateController,
             KeyguardEnvironment keyguardEnvironment,
             ForegroundServiceController foregroundServiceController,
             NotificationLockscreenUserManager userManager,
             MediaFeatureFlag mediaFeatureFlag) {
+        mDebugNotificationFilter = debugNotificationFilter;
         mStatusBarStateController = statusBarStateController;
         mKeyguardEnvironment = keyguardEnvironment;
         mForegroundServiceController = foregroundServiceController;
@@ -69,6 +73,10 @@
      */
     public boolean shouldFilterOut(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.getSbn();
+        if (mDebugNotificationFilter.shouldFilterOut(entry)) {
+            return true;
+        }
+
         if (!(mKeyguardEnvironment.isDeviceProvisioned()
                 || showNotificationEvenIfUnprovisioned(sbn))) {
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
new file mode 100644
index 0000000..df54ccd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
+import javax.inject.Inject
+
+/** A small coordinator which filters out notifications from non-allowed apps. */
+@CoordinatorScope
+class DebugModeCoordinator @Inject constructor(
+    private val debugModeFilterProvider: DebugModeFilterProvider
+) : Coordinator {
+
+    override fun attach(pipeline: NotifPipeline) {
+        pipeline.addPreGroupFilter(preGroupFilter)
+        debugModeFilterProvider.registerInvalidationListener(preGroupFilter::invalidateList)
+    }
+
+    private val preGroupFilter = object : NotifFilter("DebugModeCoordinator") {
+        override fun shouldFilterOut(entry: NotificationEntry, now: Long) =
+            debugModeFilterProvider.shouldFilterOut(entry)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 02649ba..d21d5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -47,6 +47,7 @@
     gutsCoordinator: GutsCoordinator,
     communalCoordinator: CommunalCoordinator,
     conversationCoordinator: ConversationCoordinator,
+    debugModeCoordinator: DebugModeCoordinator,
     groupCountCoordinator: GroupCountCoordinator,
     mediaCoordinator: MediaCoordinator,
     preparationCoordinator: PreparationCoordinator,
@@ -86,6 +87,7 @@
         mCoordinators.add(deviceProvisionedCoordinator)
         mCoordinators.add(bubbleCoordinator)
         mCoordinators.add(communalCoordinator)
+        mCoordinators.add(debugModeCoordinator)
         mCoordinators.add(conversationCoordinator)
         mCoordinators.add(groupCountCoordinator)
         mCoordinators.add(mediaCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
new file mode 100644
index 0000000..d16d76a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.collection.provider
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Build
+import android.util.Log
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.Assert
+import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.isNotEmpty
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A debug mode provider which is used by both the legacy and new notification pipelines to
+ * block unwanted notifications from appearing to the user, primarily for integration testing.
+ *
+ * The only configuration is a list of allowed packages.  When this list is empty, the feature is
+ * disabled.  When SystemUI starts up, this feature is disabled.
+ *
+ * To enabled filtering, provide the list of packages in a comma-separated list using the command:
+ *
+ * `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE
+ *          --esal allowed_packages <comma-separated-packages>`
+ *
+ * To disable filtering, send the action without a list:
+ *
+ * `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE`
+ *
+ * NOTE: this feature only works on debug builds, and when the broadcaster is root.
+ */
+@SysUISingleton
+class DebugModeFilterProvider @Inject constructor(
+    private val context: Context,
+    dumpManager: DumpManager
+) : Dumpable {
+    private var allowedPackages: List<String> = emptyList()
+    private val listeners = ListenerSet<Runnable>()
+
+    init {
+        dumpManager.registerDumpable(this)
+    }
+
+    /**
+     * Register a runnable to be invoked when the allowed packages changes, which would mean the
+     * result of [shouldFilterOut] may have changed for some entries.
+     */
+    fun registerInvalidationListener(listener: Runnable) {
+        Assert.isMainThread()
+        if (!Build.isDebuggable()) {
+            return
+        }
+        val needsInitialization = listeners.isEmpty()
+        listeners.addIfAbsent(listener)
+        if (needsInitialization) {
+            val filter = IntentFilter().apply { addAction(ACTION_SET_NOTIF_DEBUG_MODE) }
+            val permission = NOTIF_DEBUG_MODE_PERMISSION
+            context.registerReceiver(mReceiver, filter, permission, null)
+            Log.d(TAG, "Registered: $mReceiver")
+        }
+    }
+
+    /**
+     * Determine if the given entry should be hidden from the user in debug mode.
+     * Will always return false in release.
+     */
+    fun shouldFilterOut(entry: NotificationEntry): Boolean {
+        if (allowedPackages.isEmpty()) {
+            return false
+        }
+        return entry.sbn.packageName !in allowedPackages
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("initialized: ${listeners.isNotEmpty()}")
+        pw.println("allowedPackages: ${allowedPackages.size}")
+        allowedPackages.forEachIndexed { i, pkg ->
+            pw.println("  [$i]: $pkg")
+        }
+    }
+
+    private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent?) {
+            val action = intent?.action
+            if (ACTION_SET_NOTIF_DEBUG_MODE == action) {
+                allowedPackages = intent.extras?.getStringArrayList(EXTRA_ALLOWED_PACKAGES)
+                    ?: emptyList()
+                Log.d(TAG, "Updated allowedPackages: $allowedPackages")
+                listeners.forEach(Runnable::run)
+            } else {
+                Log.d(TAG, "Malformed intent: $intent")
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "DebugModeFilterProvider"
+        private const val ACTION_SET_NOTIF_DEBUG_MODE =
+            "com.android.systemui.action.SET_NOTIF_DEBUG_MODE"
+        private const val NOTIF_DEBUG_MODE_PERMISSION =
+            "com.android.systemui.permission.NOTIF_DEBUG_MODE"
+        private const val EXTRA_ALLOWED_PACKAGES = "allowed_packages"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 84f0955..38f3c39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.interruption.HeadsUpController
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
@@ -65,6 +66,7 @@
     private val notifPipelineFlags: NotifPipelineFlags,
     private val notificationListener: NotificationListener,
     private val entryManager: NotificationEntryManager,
+    private val debugModeFilterProvider: DebugModeFilterProvider,
     private val legacyRanker: NotificationRankingManager,
     private val commonNotifCollection: Lazy<CommonNotifCollection>,
     private val notifPipeline: Lazy<NotifPipeline>,
@@ -134,6 +136,9 @@
             headsUpController.attach(entryManager, headsUpManager)
             groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
             groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
+            debugModeFilterProvider.registerInvalidationListener {
+                entryManager.updateNotifications("debug mode filter changed")
+            }
 
             entryManager.initialize(notificationListener, legacyRanker)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 2eb2065..63cb4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -579,14 +579,24 @@
         if (contentView.hasOverlappingRendering()) {
             int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
                     : LAYER_TYPE_HARDWARE;
-            int currentLayerType = contentView.getLayerType();
-            if (currentLayerType != layerType) {
-                contentView.setLayerType(layerType, null);
-            }
+            contentView.setLayerType(layerType, null);
         }
         contentView.setAlpha(contentAlpha);
+        // After updating the current view, reset all views.
+        if (contentAlpha == 1f) {
+            resetAllContentAlphas();
+        }
     }
 
+    /**
+     * If a subclass's {@link #getContentView()} returns different views depending on state,
+     * this method is an opportunity to reset the alpha of ALL content views, not just the
+     * current one, which may prevent a content view that is temporarily hidden from being reset.
+     *
+     * This should setAlpha(1.0f) and setLayerType(LAYER_TYPE_NONE) for all content views.
+     */
+    protected void resetAllContentAlphas() {}
+
     @Override
     protected void applyRoundness() {
         super.applyRoundness();
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 82ed34c..f898470 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
@@ -2160,15 +2160,6 @@
     }
 
     public void setExpandAnimationRunning(boolean expandAnimationRunning) {
-        View contentView;
-        if (mIsSummaryWithChildren) {
-            contentView =  mChildrenContainer;
-        } else {
-            contentView = getShowingLayout();
-        }
-        if (mGuts != null && mGuts.isExposed()) {
-            contentView = mGuts;
-        }
         if (expandAnimationRunning) {
             setAboveShelf(true);
             mExpandAnimationRunning = true;
@@ -2181,9 +2172,7 @@
             if (mGuts != null) {
                 mGuts.setAlpha(1.0f);
             }
-            if (contentView != null) {
-                contentView.setAlpha(1.0f);
-            }
+            resetAllContentAlphas();
             setExtraWidthForClipping(0.0f);
             if (mNotificationParent != null) {
                 mNotificationParent.setExtraWidthForClipping(0.0f);
@@ -2632,10 +2621,8 @@
             mPrivateLayout.animate().cancel();
             if (mChildrenContainer != null) {
                 mChildrenContainer.animate().cancel();
-                mChildrenContainer.setAlpha(1f);
             }
-            mPublicLayout.setAlpha(1f);
-            mPrivateLayout.setAlpha(1f);
+            resetAllContentAlphas();
             mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
             updateChildrenVisibility();
         } else {
@@ -2662,7 +2649,10 @@
                     .alpha(0f)
                     .setStartDelay(delay)
                     .setDuration(duration)
-                    .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE));
+                    .withEndAction(() -> {
+                        hiddenView.setVisibility(View.INVISIBLE);
+                        resetAllContentAlphas();
+                    });
         }
         for (View showView : shownChildren) {
             showView.setVisibility(View.VISIBLE);
@@ -2785,12 +2775,7 @@
         if (wasAppearing) {
             // During the animation the visible view might have changed, so let's make sure all
             // alphas are reset
-            if (mChildrenContainer != null) {
-                mChildrenContainer.setAlpha(1.0f);
-            }
-            for (NotificationContentView l : mLayouts) {
-                l.setAlpha(1.0f);
-            }
+            resetAllContentAlphas();
             if (FADE_LAYER_OPTIMIZATION_ENABLED) {
                 setNotificationFaded(false);
             } else {
@@ -2801,6 +2786,18 @@
         }
     }
 
+    @Override
+    protected void resetAllContentAlphas() {
+        mPrivateLayout.setAlpha(1f);
+        mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
+        mPublicLayout.setAlpha(1f);
+        mPublicLayout.setLayerType(LAYER_TYPE_NONE, null);
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setAlpha(1f);
+            mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+        }
+    }
+
     /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
     @Override
     public boolean isNotificationFaded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a9cc3237..e658468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -59,6 +59,7 @@
     private float mMaxHeadsUpTranslation;
     private boolean mDismissAllInProgress;
     private int mLayoutMinHeight;
+    private int mLayoutMaxHeight;
     private NotificationShelf mShelf;
     private int mZDistanceBetweenElements;
     private int mBaseZHeight;
@@ -326,6 +327,14 @@
         mLayoutHeight = layoutHeight;
     }
 
+    public void setLayoutMaxHeight(int maxLayoutHeight) {
+        mLayoutMaxHeight = maxLayoutHeight;
+    }
+
+    public int getLayoutMaxHeight() {
+        return mLayoutMaxHeight;
+    }
+
     public float getTopPadding() {
         return mTopPadding;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ffe6e4b..e1dbf4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1111,6 +1111,7 @@
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmHeightAndPadding() {
         mAmbientState.setLayoutHeight(getLayoutHeight());
+        mAmbientState.setLayoutMaxHeight(mMaxLayoutHeight);
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
     }
@@ -3972,6 +3973,7 @@
             updateChronometers();
             requestChildrenUpdate();
             updateUseRoundedRectClipping();
+            updateDismissBehavior();
         }
     }
 
@@ -4935,6 +4937,10 @@
         StringBuilder sb = new StringBuilder("[")
                 .append(this.getClass().getSimpleName()).append(":")
                 .append(" pulsing=").append(mPulsing ? "T" : "f")
+                .append(" expanded=").append(mIsExpanded ? "T" : "f")
+                .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f")
+                .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f")
+                .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f")
                 .append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility()))
                 .append(" alpha=").append(getAlpha())
                 .append(" scrollY=").append(mAmbientState.getScrollY())
@@ -5462,7 +5468,7 @@
         // On the split keyguard, dismissing with clipping without a visual boundary looks odd,
         // so let's use the content dismiss behavior instead.
         boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
-                || mStatusBarState != StatusBarState.KEYGUARD;
+                || (mStatusBarState != StatusBarState.KEYGUARD && mIsExpanded);
         if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
             mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
             for (int i = 0; i < getChildCount(); i++) {
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 015edb8..2c70a5f 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
@@ -29,6 +29,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -60,6 +61,7 @@
     @VisibleForTesting float mHeadsUpInset;
     private int mPinnedZTranslationExtra;
     private float mNotificationScrimPadding;
+    private int mCloseHandleUnderlapHeight;
 
     public StackScrollAlgorithm(
             Context context,
@@ -85,6 +87,7 @@
                 R.dimen.heads_up_pinned_elevation);
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
         mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+        mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
     }
 
     /**
@@ -459,13 +462,17 @@
                                 && !hasOngoingNotifs(algorithmState));
             }
         } else {
-            if (view != ambientState.getTrackedHeadsUpRow()) {
+            if (view instanceof EmptyShadeView) {
+                float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+                        - ambientState.getStackY();
+                viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
+            } else if (view != ambientState.getTrackedHeadsUpRow()) {
                 if (ambientState.isExpansionChanging()) {
                     // We later update shelf state, then hide views below the shelf.
                     viewState.hidden = false;
                     viewState.inShelf = algorithmState.firstViewInShelf != null
                             && i >= algorithmState.visibleChildren.indexOf(
-                                    algorithmState.firstViewInShelf);
+                            algorithmState.firstViewInShelf);
                 } else if (ambientState.getShelf() != null) {
                     // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
                     // to shelf start, thereby hiding all notifications (except the first one, which
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 4e68b19..097c0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -519,8 +519,6 @@
 
     private WeakReference<CommunalSource> mCommunalSource;
 
-    private final CommunalSource.Callback mCommunalSourceCallback;
-
     private final CommandQueue mCommandQueue;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final UserManager mUserManager;
@@ -906,9 +904,6 @@
 
         mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
         mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
-        mCommunalSourceCallback = () -> {
-            mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
-        };
 
         mCommunalSourceMonitorCallback = (source) -> {
             mUiExecutor.execute(() -> setCommunalSource(source));
@@ -4713,7 +4708,6 @@
         CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null;
 
         if (existingSource != null) {
-            existingSource.removeCallback(mCommunalSourceCallback);
             mCommunalViewController.show(null /*source*/);
         }
 
@@ -4722,7 +4716,6 @@
         CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null;
         // Set source and register callback
         if (currentSource != null && mCommunalViewController != null) {
-            currentSource.addCallback(mCommunalSourceCallback);
             mCommunalViewController.show(source);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 8df7b45..e3b4caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -45,6 +45,10 @@
 
 /**
  * Base class for dialogs that should appear over panels and keyguard.
+ *
+ * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to
+ * listeners on whether this dialog is showing.
+ *
  * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
  * and dismisses itself when it receives the broadcast.
  */
@@ -54,8 +58,9 @@
             "persist.systemui.flag_tablet_dialog_width";
 
     private final Context mContext;
-    private final DismissReceiver mDismissReceiver;
+    @Nullable private final DismissReceiver mDismissReceiver;
     private final Handler mHandler = new Handler();
+    @Nullable private final SystemUIDialogManager mDialogManager;
 
     private int mLastWidth = Integer.MIN_VALUE;
     private int mLastHeight = Integer.MIN_VALUE;
@@ -66,11 +71,27 @@
         this(context, R.style.Theme_SystemUI_Dialog);
     }
 
+    public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
+        this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
+    }
+
     public SystemUIDialog(Context context, int theme) {
         this(context, theme, true /* dismissOnDeviceLock */);
     }
 
+    public SystemUIDialog(Context context, int theme, SystemUIDialogManager dialogManager) {
+        this(context, theme, true /* dismissOnDeviceLock */, dialogManager);
+    }
+
     public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
+        this(context, theme, dismissOnDeviceLock, null);
+    }
+
+    /**
+     * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing.
+     */
+    public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+            SystemUIDialogManager dialogManager) {
         super(context, theme);
         mContext = context;
 
@@ -80,6 +101,7 @@
         getWindow().setAttributes(attrs);
 
         mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
+        mDialogManager = dialogManager;
     }
 
     @Override
@@ -145,6 +167,10 @@
             mDismissReceiver.register();
         }
 
+        if (mDialogManager != null) {
+            mDialogManager.setShowing(this, true);
+        }
+
         // Listen for configuration changes to resize this dialog window. This is mostly necessary
         // for foldables that often go from large <=> small screen when folding/unfolding.
         ViewRootImpl.addConfigCallback(this);
@@ -158,6 +184,10 @@
             mDismissReceiver.unregister();
         }
 
+        if (mDialogManager != null) {
+            mDialogManager.setShowing(this, false);
+        }
+
         ViewRootImpl.removeConfigCallback(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
new file mode 100644
index 0000000..204f710
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.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.systemui.statusbar.phone;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * Register dialogs to this manager if extraneous affordances (like the UDFPS sensor area)
+ * should be hidden from the screen when the dialog shows.
+ *
+ * Currently, only used if UDFPS is supported on the device; however, can be extended in the future
+ * for other use cases.
+ */
+@SysUISingleton
+public class SystemUIDialogManager implements Dumpable {
+    private final StatusBarKeyguardViewManager mKeyguardViewManager;
+
+    private final Set<SystemUIDialog> mDialogsShowing = new HashSet<>();
+    private final Set<Listener> mListeners = new HashSet<>();
+
+    @Inject
+    public SystemUIDialogManager(
+            DumpManager dumpManager,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        dumpManager.registerDumpable(this);
+        mKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    /**
+     * Whether listeners should hide affordances like the UDFPS sensor icon.
+     */
+    public boolean shouldHideAffordance() {
+        return !mDialogsShowing.isEmpty();
+    }
+
+    /**
+     * Register a listener to receive callbacks.
+     */
+    public void registerListener(@NonNull Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Unregister a listener from receiving callbacks.
+     */
+    public void unregisterListener(@NonNull Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    void setShowing(SystemUIDialog dialog, boolean showing) {
+        final boolean wasHidingAffordances = shouldHideAffordance();
+        if (showing) {
+            mDialogsShowing.add(dialog);
+        } else {
+            mDialogsShowing.remove(dialog);
+        }
+
+        if (wasHidingAffordances != shouldHideAffordance()) {
+            updateDialogListeners();
+        }
+    }
+
+    private void updateDialogListeners() {
+        if (shouldHideAffordance()) {
+            mKeyguardViewManager.resetAlternateAuth(true);
+        }
+
+        for (Listener listener : mListeners) {
+            listener.shouldHideAffordances(shouldHideAffordance());
+        }
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("listeners:");
+        for (Listener listener : mListeners) {
+            pw.println("\t" + listener);
+        }
+        pw.println("dialogs tracked:");
+        for (SystemUIDialog dialog : mDialogsShowing) {
+            pw.println("\t" + dialog);
+        }
+    }
+
+    /** SystemUIDialogManagerListener */
+    public interface Listener {
+        /**
+         * Callback where shouldHide=true if listeners should hide their views that may overlap
+         * a showing dialog.
+         */
+        void shouldHideAffordances(boolean shouldHide);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index bad0c37..c57dbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -16,9 +16,7 @@
 
 package com.android.systemui.util.service;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,6 +24,8 @@
 import android.os.IBinder;
 import android.util.Log;
 
+import com.android.systemui.dagger.qualifiers.Main;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -35,6 +35,8 @@
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+
 /**
  * {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection}
  * that enables monitoring the status of a binder connection. It also aides in automatically
@@ -119,17 +121,17 @@
      * Default constructor for {@link ObservableServiceConnection}.
      * @param context The context from which the service will be bound with.
      * @param serviceIntent The intent to  bind service with.
-     * @param flags The flags to use during the binding
      * @param executor The executor for connection callbacks to be delivered on
      * @param transformer A {@link ServiceTransformer} for transforming the resulting service
      *                    into a desired type.
      */
+    @Inject
     public ObservableServiceConnection(Context context, Intent serviceIntent,
-            @Context.BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
+            @Main Executor executor,
             ServiceTransformer<T> transformer) {
         mContext = context;
         mServiceIntent = serviceIntent;
-        mFlags = flags;
+        mFlags = Context.BIND_AUTO_CREATE;
         mExecutor = executor;
         mTransformer = transformer;
         mCallbacks = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6dd6d6d..4600bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -56,6 +56,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -66,7 +67,6 @@
 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.protolog.ShellProtoLogImpl;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
@@ -114,7 +114,7 @@
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
     private final Optional<ShellCommandHandler> mShellCommandHandler;
-    private final Optional<SizeCompatUI> mSizeCompatUIOptional;
+    private final Optional<CompatUI> mCompatUIOptional;
     private final Optional<DragAndDrop> mDragAndDropOptional;
 
     private final CommandQueue mCommandQueue;
@@ -132,7 +132,7 @@
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
-    private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback;
+    private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
 
     @Inject
@@ -143,7 +143,7 @@
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutoutOptional,
             Optional<ShellCommandHandler> shellCommandHandler,
-            Optional<SizeCompatUI> sizeCompatUIOptional,
+            Optional<CompatUI> sizeCompatUIOptional,
             Optional<DragAndDrop> dragAndDropOptional,
             CommandQueue commandQueue,
             ConfigurationController configurationController,
@@ -169,7 +169,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mProtoTracer = protoTracer;
         mShellCommandHandler = shellCommandHandler;
-        mSizeCompatUIOptional = sizeCompatUIOptional;
+        mCompatUIOptional = sizeCompatUIOptional;
         mDragAndDropOptional = dragAndDropOptional;
         mSysUiMainExecutor = sysUiMainExecutor;
     }
@@ -185,7 +185,7 @@
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
-        mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
+        mCompatUIOptional.ifPresent(this::initCompatUi);
         mDragAndDropOptional.ifPresent(this::initDragAndDrop);
     }
 
@@ -391,14 +391,14 @@
     }
 
     @VisibleForTesting
-    void initSizeCompatUi(SizeCompatUI sizeCompatUI) {
-        mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+    void initCompatUi(CompatUI sizeCompatUI) {
+        mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
             @Override
             public void onKeyguardOccludedChanged(boolean occluded) {
                 sizeCompatUI.onKeyguardOccludedChanged(occluded);
             }
         };
-        mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
+        mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
     }
 
     void initDragAndDrop(DragAndDrop dragAndDrop) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 98ce138..c873804 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -111,6 +112,8 @@
     @Mock
     private FalsingCollector mFalsingCollector;
     @Mock
+    private FalsingManager mFalsingManager;
+    @Mock
     private GlobalSettings mGlobalSettings;
     @Mock
     private UserSwitcherController mUserSwitcherController;
@@ -147,8 +150,8 @@
                 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
-                mConfigurationController, mFalsingCollector, mUserSwitcherController,
-                mGlobalSettings).create(mSecurityCallback);
+                mConfigurationController, mFalsingCollector, mFalsingManager,
+                mUserSwitcherController, mGlobalSettings).create(mSecurityCallback);
     }
 
     @Test
@@ -185,14 +188,14 @@
     public void onResourcesUpdate_callsThroughOnRotationChange() {
         // Rotation is the same, shouldn't cause an update
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         // Update rotation. Should trigger update
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
 
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
     }
 
@@ -250,7 +253,7 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
     }
 
@@ -262,7 +265,7 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+        verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
     }
 
@@ -274,7 +277,7 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index ea7940a..7776285 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -47,7 +47,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
@@ -83,7 +83,7 @@
     @Mock
     private GlobalSettings mGlobalSettings;
     @Mock
-    private FalsingCollector mFalsingCollector;
+    private FalsingManager mFalsingManager;
     @Mock
     private UserSwitcherController mUserSwitcherController;
     @Mock
@@ -117,7 +117,7 @@
 
     @Test
     public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
-        mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+        mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         int halfWidthMeasureSpec =
@@ -129,7 +129,7 @@
 
     @Test
     public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -141,7 +141,7 @@
         int imeInsetAmount = 100;
         int systemBarInsetAmount = 10;
 
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
@@ -166,7 +166,7 @@
         int imeInsetAmount = 0;
         int systemBarInsetAmount = 10;
 
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
@@ -187,7 +187,7 @@
 
     private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
         int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
-        mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingCollector,
+        mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
                 mUserSwitcherController);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -298,7 +298,7 @@
 
     private void setupUserSwitcher() {
         mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
-                mGlobalSettings, mFalsingCollector, mUserSwitcherController);
+                mGlobalSettings, mFalsingManager, mUserSwitcherController);
     }
 
     private ArrayList<UserRecord> buildUserRecords(int count) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 241b02d..1dea678 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -169,6 +170,8 @@
     private TypedArray mBrightnessValues;
     @Mock
     private TypedArray mBrightnessBacklight;
+    @Mock
+    private SystemUIDialogManager mSystemUIDialogManager;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -178,8 +181,6 @@
     @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
     private ScreenLifecycle.Observer mScreenObserver;
 
-    @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor;
-
     @Before
     public void setUp() {
         setUpResources();
@@ -237,7 +238,8 @@
                 mHandler,
                 mConfigurationController,
                 mSystemClock,
-                mUnlockedScreenOffAnimationController);
+                mUnlockedScreenOffAnimationController,
+                mSystemUIDialogManager);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -641,12 +643,12 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
         moveEvent.recycle();
 
-        // THEN low-tick haptic is played
+        // THEN tick haptic is played
         verify(mVibrator).vibrate(
                 anyInt(),
                 anyString(),
                 any(),
-                eq("udfps-onStart-tick"),
+                eq("udfps-onStart-click"),
                 eq(UdfpsController.VIBRATION_ATTRIBUTES));
 
         // THEN make sure vibration attributes has so that it always will play the haptic,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 1cf21ac..0ae3c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -92,6 +93,8 @@
     @Mock
     private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     @Mock
+    private SystemUIDialogManager mDialogManager;
+    @Mock
     private UdfpsController mUdfpsController;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
@@ -130,6 +133,7 @@
                 mSystemClock,
                 mKeyguardStateController,
                 mUnlockedScreenOffAnimationController,
+                mDialogManager,
                 mUdfpsController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
index abc2099..9a9b7c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -29,6 +29,8 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,6 +47,8 @@
     @Mock
     private CommunalConditionsMonitor mCommunalConditionsMonitor;
 
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -56,7 +60,7 @@
             return null;
         }).when(mCommunalConditionsMonitor).addCallback(any());
 
-        mMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
+        mMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
         final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
         updater.start();
         clearInvocations(mCommunalManager);
@@ -65,6 +69,7 @@
     @Test
     public void testUpdateSystemService_false() {
         mMonitor.setSource(null);
+        mExecutor.runAllReady();
         verify(mCommunalManager).setCommunalViewShowing(false);
     }
 
@@ -72,6 +77,7 @@
     public void testUpdateSystemService_true() {
         final CommunalSource source = mock(CommunalSource.class);
         mMonitor.setSource(source);
+        mExecutor.runAllReady();
         verify(mCommunalManager).setCommunalViewShowing(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index df9a2cb..409dd94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -32,6 +32,8 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,11 +54,12 @@
     @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
 
     private CommunalSourceMonitor mCommunalSourceMonitor;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
+        mCommunalSourceMonitor = new CommunalSourceMonitor(mExecutor, mCommunalConditionsMonitor);
     }
 
     @Test
@@ -64,7 +67,7 @@
         final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
         final CommunalSource source = mock(CommunalSource.class);
 
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
         mCommunalSourceMonitor.addCallback(callback);
         setConditionsMet(true);
 
@@ -78,7 +81,7 @@
 
         mCommunalSourceMonitor.addCallback(callback);
         mCommunalSourceMonitor.removeCallback(callback);
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
 
         verify(callback, never()).onSourceAvailable(any());
     }
@@ -91,7 +94,7 @@
         mCommunalSourceMonitor.addCallback(callback);
         setConditionsMet(true);
         clearInvocations(callback);
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
 
         verifyOnSourceAvailableCalledWith(callback, source);
     }
@@ -103,7 +106,7 @@
 
         mCommunalSourceMonitor.addCallback(callback);
         setConditionsMet(false);
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
 
         verify(callback, never()).onSourceAvailable(any());
     }
@@ -114,7 +117,7 @@
         final CommunalSource source = mock(CommunalSource.class);
 
         mCommunalSourceMonitor.addCallback(callback);
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
 
         // The callback should not have executed since communal is disabled.
         verify(callback, never()).onSourceAvailable(any());
@@ -130,7 +133,7 @@
         final CommunalSource source = mock(CommunalSource.class);
 
         mCommunalSourceMonitor.addCallback(callback);
-        mCommunalSourceMonitor.setSource(source);
+        setSource(source);
         setConditionsMet(true);
         verifyOnSourceAvailableCalledWith(callback, source);
 
@@ -151,9 +154,16 @@
     // Pushes an update on whether the communal conditions are met, assuming that a callback has
     // been registered with the communal conditions monitor.
     private void setConditionsMet(boolean value) {
+        mExecutor.runAllReady();
         verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
         final CommunalConditionsMonitor.Callback conditionsCallback =
                 mConditionsCallbackCaptor.getValue();
         conditionsCallback.onConditionsChanged(value);
+        mExecutor.runAllReady();
+    }
+
+    private void setSource(CommunalSource source) {
+        mCommunalSourceMonitor.setSource(source);
+        mExecutor.runAllReady();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
index 2ed38a4..1d9a059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -26,14 +27,16 @@
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 
-import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeExecutor;;
+import com.android.systemui.util.ref.GcWeakReference;
 import com.android.systemui.util.time.FakeSystemClock;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +55,35 @@
     private static final int RETRY_DELAY_MS = 1000;
     private static final int CONNECTION_MIN_DURATION_MS = 5000;
 
+    // A simple implementation of {@link CommunalSource.Observer} to capture a callback value.
+    // Used to ensure the references to a {@link CommunalSource.Observer.Callback} can be fully
+    // removed.
+    private static class FakeObserver implements CommunalSource.Observer {
+        public GcWeakReference<Callback> mLastCallback;
+
+        @Override
+        public void addCallback(Callback callback) {
+            mLastCallback = new GcWeakReference<>(callback);
+        }
+
+        @Override
+        public void removeCallback(Callback callback) {
+            if (mLastCallback.get() == callback) {
+                mLastCallback = null;
+            }
+        }
+    }
+
+    // A simple implementation of {@link CommunalSource} to capture callback values. This
+    // implementation better emulates the {@link WeakReference} wrapping behavior of
+    // {@link CommunalSource} implementations than a mock.
+    private static class FakeSource implements CommunalSource {
+        @Override
+        public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
+            return null;
+        }
+    }
+
     @Mock
     private Context mContext;
 
@@ -61,8 +93,7 @@
     private FakeSystemClock mFakeClock = new FakeSystemClock();
     private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock);
 
-    @Mock
-    private CommunalSource mSource;
+    private FakeSource mSource = new FakeSource();
 
     @Mock
     private CommunalSourceMonitor mCommunalSourceMonitor;
@@ -71,7 +102,9 @@
     private CommunalSource.Connector mConnector;
 
     @Mock
-    private CommunalSource.Observer mObserver;
+    private CommunalSource.Connection mConnection;
+
+    private FakeObserver mObserver = new FakeObserver();
 
     private CommunalSourcePrimer mPrimer;
 
@@ -93,35 +126,35 @@
                 mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
     }
 
+    private CommunalSource.Connection.Callback captureCallbackAndSend(
+            CommunalSource.Connector connector, Optional<CommunalSource> source) {
+        ArgumentCaptor<CommunalSource.Connection.Callback> connectionCallback =
+                ArgumentCaptor.forClass(CommunalSource.Connection.Callback.class);
+
+        verify(connector).connect(connectionCallback.capture());
+        Mockito.clearInvocations(connector);
+
+        final CommunalSource.Connection.Callback callback = connectionCallback.getValue();
+        callback.onSourceEstablished(source);
+
+        return callback;
+    }
+
     @Test
     public void testConnect() {
-        when(mConnector.connect()).thenReturn(
-                CallbackToFutureAdapter.getFuture(completer -> {
-                    completer.set(Optional.of(mSource));
-                    return "test";
-                }));
-
         mPrimer.onBootCompleted();
-        mFakeExecutor.runAllReady();
+        captureCallbackAndSend(mConnector, Optional.of(mSource));
         verify(mCommunalSourceMonitor).setSource(mSource);
     }
 
     @Test
     public void testRetryOnBindFailure() throws Exception {
-        when(mConnector.connect()).thenReturn(
-                CallbackToFutureAdapter.getFuture(completer -> {
-                    completer.set(Optional.empty());
-                    return "test";
-                }));
-
         mPrimer.onBootCompleted();
-        mFakeExecutor.runAllReady();
 
         // Verify attempts happen. Note that we account for the retries plus initial attempt, which
         // is not scheduled.
         for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
-            verify(mConnector, times(1)).connect();
-            clearInvocations(mConnector);
+            captureCallbackAndSend(mConnector, Optional.empty());
             mFakeExecutor.advanceClockToNext();
             mFakeExecutor.runAllReady();
         }
@@ -131,76 +164,42 @@
 
     @Test
     public void testRetryOnDisconnectFailure() throws Exception {
-        when(mConnector.connect()).thenReturn(
-                CallbackToFutureAdapter.getFuture(completer -> {
-                    completer.set(Optional.of(mSource));
-                    return "test";
-                }));
-
         mPrimer.onBootCompleted();
-        mFakeExecutor.runAllReady();
-
         // Verify attempts happen. Note that we account for the retries plus initial attempt, which
         // is not scheduled.
         for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
-            verify(mConnector, times(1)).connect();
-            clearInvocations(mConnector);
-            ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
-                    ArgumentCaptor.forClass(CommunalSource.Callback.class);
-            verify(mSource).addCallback(callbackCaptor.capture());
-            clearInvocations(mSource);
+            final CommunalSource.Connection.Callback callback =
+                    captureCallbackAndSend(mConnector, Optional.of(mSource));
             verify(mCommunalSourceMonitor).setSource(Mockito.notNull());
             clearInvocations(mCommunalSourceMonitor);
-            callbackCaptor.getValue().onDisconnected();
+            callback.onDisconnected();
             mFakeExecutor.advanceClockToNext();
             mFakeExecutor.runAllReady();
         }
 
-        verify(mConnector, never()).connect();
+        verify(mConnector, never()).connect(any());
     }
 
     @Test
     public void testAttemptOnPackageChange() {
-        when(mConnector.connect()).thenReturn(
-                CallbackToFutureAdapter.getFuture(completer -> {
-                    completer.set(Optional.empty());
-                    return "test";
-                }));
-
         mPrimer.onBootCompleted();
-        mFakeExecutor.runAllReady();
+        captureCallbackAndSend(mConnector, Optional.empty());
 
-        final ArgumentCaptor<CommunalSource.Observer.Callback> callbackCaptor =
-                ArgumentCaptor.forClass(CommunalSource.Observer.Callback.class);
-        verify(mObserver).addCallback(callbackCaptor.capture());
+        mObserver.mLastCallback.get().onSourceChanged();
 
-        clearInvocations(mConnector);
-        callbackCaptor.getValue().onSourceChanged();
-
-        verify(mConnector, times(1)).connect();
+        verify(mConnector, times(1)).connect(any());
     }
 
     @Test
     public void testDisconnect() {
-        final ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
-                ArgumentCaptor.forClass(CommunalSource.Callback.class);
-
-        when(mConnector.connect()).thenReturn(
-                CallbackToFutureAdapter.getFuture(completer -> {
-                    completer.set(Optional.of(mSource));
-                    return "test";
-                }));
-
         mPrimer.onBootCompleted();
-        mFakeExecutor.runAllReady();
+        final CommunalSource.Connection.Callback callback =
+                captureCallbackAndSend(mConnector, Optional.of(mSource));
         verify(mCommunalSourceMonitor).setSource(mSource);
-        verify(mSource).addCallback(callbackCaptor.capture());
 
-        clearInvocations(mConnector);
         mFakeClock.advanceTime(CONNECTION_MIN_DURATION_MS + 1);
-        callbackCaptor.getValue().onDisconnected();
-        mFakeExecutor.runAllReady();
+        callback.onDisconnected();
 
-        verify(mConnector).connect();
+        verify(mConnector).connect(any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index bf5522c..e3a7e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -62,6 +62,7 @@
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -117,6 +118,7 @@
     @Mock private StatusBar mStatusBar;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock private SystemUIDialogManager mDialogManager;
 
     private TestableLooper mTestableLooper;
 
@@ -162,7 +164,8 @@
                 mPackageManager,
                 Optional.of(mStatusBar),
                 mKeyguardUpdateMonitor,
-                mDialogLaunchAnimator);
+                mDialogLaunchAnimator,
+                mDialogManager);
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
         ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 81bcbfb..d7c00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,7 +42,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -69,8 +67,6 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
-import com.airbnb.lottie.LottieAnimationView;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -110,8 +106,6 @@
     private @Mock ConfigurationController mConfigurationController;
     private @Mock Vibrator mVibrator;
     private @Mock AuthRippleController mAuthRippleController;
-    private @Mock LottieAnimationView mAodFp;
-    private @Mock LayoutInflater mLayoutInflater;
     private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
 
     private LockIconViewController mLockIconViewController;
@@ -149,7 +143,6 @@
 
         when(mLockIconView.getResources()).thenReturn(mResources);
         when(mLockIconView.getContext()).thenReturn(mContext);
-        when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp);
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
         Rect windowBounds = new Rect(0, 0, 800, 1200);
@@ -176,8 +169,7 @@
                 mDelayableExecutor,
                 mVibrator,
                 mAuthRippleController,
-                mResources,
-                mLayoutInflater
+                mResources
         );
     }
 
@@ -187,35 +179,6 @@
     }
 
     @Test
-    public void testIgnoreUdfpsWhenNotSupported() {
-        // GIVEN Udpfs sensor is NOT available
-        mLockIconViewController.init();
-        captureAttachListener();
-
-        // WHEN the view is attached
-        mAttachListener.onViewAttachedToWindow(mLockIconView);
-
-        // THEN lottie animation should NOT be inflated
-        verify(mLayoutInflater, never()).inflate(eq(R.layout.udfps_aod_lock_icon), any());
-    }
-
-    @Test
-    public void testInflateUdfpsWhenSupported() {
-        // GIVEN Udpfs sensor is available
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        mLockIconViewController.init();
-        captureAttachListener();
-
-        // WHEN the view is attached
-        mAttachListener.onViewAttachedToWindow(mLockIconView);
-
-        // THEN lottie animation should be inflated
-        verify(mLayoutInflater).inflate(eq(R.layout.udfps_aod_lock_icon), any());
-    }
-
-    @Test
     public void testUpdateFingerprintLocationOnInit() {
         // GIVEN fp sensor location is available pre-attached
         Pair<Integer, PointF> udfps = setupUdfps();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index d0b957c..649ee87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -669,7 +669,7 @@
 
     @Test
     fun testPlaybackActions_noPrevNext_usesCustom() {
-        val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
+        val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
         whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 4dac6d5..9f542f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -44,6 +44,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,6 +67,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
     private MediaOutputController mMediaOutputController;
@@ -79,7 +81,7 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                 mMediaOutputController);
         mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -169,7 +171,7 @@
     class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
 
         MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
-            super(context, mediaOutputController);
+            super(context, mediaOutputController, mDialogManager);
 
             mAdapter = mMediaOutputBaseAdapter;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index d71d98e..a84a803 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -55,6 +55,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -94,6 +95,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private Context mSpyContext;
     private MediaOutputController mMediaOutputController;
@@ -116,7 +118,7 @@
 
         mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -160,7 +162,7 @@
     public void start_withoutPackageName_verifyMediaControllerInit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         mMediaOutputController.start(mCb);
 
@@ -181,7 +183,7 @@
     public void stop_withoutPackageName_verifyMediaControllerDeinit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         mMediaOutputController.start(mCb);
 
@@ -452,7 +454,7 @@
     public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         assertThat(mMediaOutputController.getNotificationIcon()).isNull();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 8a3ea56..ada8d35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,6 +68,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputDialog mMediaOutputDialog;
     private MediaOutputController mMediaOutputController;
@@ -76,10 +78,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger);
+                mMediaOutputController, mUiEventLogger, mDialogManager);
         mMediaOutputDialog.show();
 
         when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -125,7 +127,7 @@
     // and verify if the calling times increases.
     public void onCreate_ShouldLogVisibility() {
         MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger);
+                mMediaOutputController, mUiEventLogger, mDialogManager);
         testDialog.show();
 
         testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index e8cd6c8..b114452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -66,6 +67,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputGroupDialog mMediaOutputGroupDialog;
     private MediaOutputController mMediaOutputController;
@@ -75,10 +77,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
-                mMediaOutputController);
+                mMediaOutputController, mDialogManager);
         mMediaOutputGroupDialog.show();
         when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index 923f018..50d94dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -23,8 +23,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.commandline.Command
-import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -35,83 +33,58 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.StringWriter
-import java.util.concurrent.Executor
 
 @SmallTest
 class MediaTttChipControllerTest : SysuiTestCase() {
 
     private lateinit var mediaTttChipController: MediaTttChipController
 
-    private val inlineExecutor = Executor { command -> command.run() }
-    private val commandRegistry = CommandRegistry(context, inlineExecutor)
-    private val pw = PrintWriter(StringWriter())
-
     @Mock
     private lateinit var windowManager: WindowManager
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        mediaTttChipController = MediaTttChipController(commandRegistry, context, windowManager)
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun constructor_addCommmandAlreadyRegistered() {
-        // Since creating the chip controller should automatically register the add command, it
-        // should throw when registering it again.
-        commandRegistry.registerCommand(
-            MediaTttChipController.ADD_CHIP_COMMAND_TAG
-        ) { EmptyCommand() }
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun constructor_removeCommmandAlreadyRegistered() {
-        // Since creating the chip controller should automatically register the remove command, it
-        // should throw when registering it again.
-        commandRegistry.registerCommand(
-            MediaTttChipController.REMOVE_CHIP_COMMAND_TAG
-        ) { EmptyCommand() }
+        mediaTttChipController = MediaTttChipController(context, windowManager)
     }
 
     @Test
-    fun addChipCommand_chipAdded() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+    fun displayChip_chipAdded() {
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
 
         verify(windowManager).addView(any(), any())
     }
 
     @Test
-    fun addChipCommand_twice_chipNotAddedTwice() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+    fun displayChip_twice_chipNotAddedTwice() {
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
         reset(windowManager)
 
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
         verify(windowManager, never()).addView(any(), any())
     }
 
     @Test
-    fun removeChipCommand_chipRemoved() {
+    fun removeChip_chipRemoved() {
         // First, add the chip
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
 
         // Then, remove it
-        commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+        mediaTttChipController.removeChip()
 
         verify(windowManager).removeView(any())
     }
 
     @Test
-    fun removeChipCommand_noAdd_viewNotRemoved() {
-        commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
+    fun removeChip_noAdd_viewNotRemoved() {
+        mediaTttChipController.removeChip()
 
         verify(windowManager, never()).removeView(any())
     }
 
     @Test
     fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
 
         val chipView = getChipView()
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -121,7 +94,7 @@
 
     @Test
     fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
-        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+        mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
 
         val chipView = getChipView()
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -131,7 +104,7 @@
 
     @Test
     fun transferSucceeded_chipTextContainsDeviceName_noLoadingIcon_undo() {
-        commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+        mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
 
         val chipView = getChipView()
         assertThat(chipView.getChipText()).contains(DEVICE_NAME)
@@ -141,57 +114,36 @@
 
     @Test
     fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
-        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
+        mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
 
         assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
     }
 
     @Test
     fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
-        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
-        commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+        mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+        mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
 
         assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
     }
 
     @Test
     fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
-        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
-        commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+        mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+        mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
 
         assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
     }
 
     @Test
     fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
-        commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+        mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
+        mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
 
         assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.GONE)
     }
 
-    private fun getMoveCloserToTransferCommand(): Array<String> =
-        arrayOf(
-            MediaTttChipController.ADD_CHIP_COMMAND_TAG,
-            DEVICE_NAME,
-            MediaTttChipController.ChipType.MOVE_CLOSER_TO_TRANSFER.name
-        )
-
-    private fun getTransferInitiatedCommand(): Array<String> =
-        arrayOf(
-            MediaTttChipController.ADD_CHIP_COMMAND_TAG,
-            DEVICE_NAME,
-            MediaTttChipController.ChipType.TRANSFER_INITIATED.name
-        )
-
-    private fun getTransferSucceededCommand(): Array<String> =
-        arrayOf(
-            MediaTttChipController.ADD_CHIP_COMMAND_TAG,
-            DEVICE_NAME,
-            MediaTttChipController.ChipType.TRANSFER_SUCCEEDED.name
-        )
-
     private fun LinearLayout.getChipText(): String =
         (this.requireViewById<TextView>(R.id.text)).text as String
 
@@ -206,14 +158,6 @@
         verify(windowManager).addView(viewCaptor.capture(), any())
         return viewCaptor.value as LinearLayout
     }
-
-    class EmptyCommand : Command {
-        override fun execute(pw: PrintWriter, args: List<String>) {
-        }
-
-        override fun help(pw: PrintWriter) {
-        }
-    }
 }
 
 private const val DEVICE_NAME = "My Tablet"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
new file mode 100644
index 0000000..4b85fa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.media.taptotransfer
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.util.concurrent.Executor
+
+@SmallTest
+class MediaTttCommandLineHelperTest : SysuiTestCase() {
+
+    private val inlineExecutor = Executor { command -> command.run() }
+    private val commandRegistry = CommandRegistry(context, inlineExecutor)
+    private val pw = PrintWriter(StringWriter())
+
+    private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
+
+    @Mock
+    private lateinit var mediaTttChipController: MediaTttChipController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mediaTttCommandLineHelper =
+            MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun constructor_addCommandAlreadyRegistered() {
+        // Since creating the chip controller should automatically register the add command, it
+        // should throw when registering it again.
+        commandRegistry.registerCommand(
+            ADD_CHIP_COMMAND_TAG
+        ) { EmptyCommand() }
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun constructor_removeCommandAlreadyRegistered() {
+        // Since creating the chip controller should automatically register the remove command, it
+        // should throw when registering it again.
+        commandRegistry.registerCommand(
+            REMOVE_CHIP_COMMAND_TAG
+        ) { EmptyCommand() }
+    }
+
+    @Test
+    fun moveCloserToTransfer_chipDisplayWithCorrectState() {
+        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+
+        verify(mediaTttChipController).displayChip(any(MoveCloserToTransfer::class.java))
+    }
+
+    @Test
+    fun transferInitiated_chipDisplayWithCorrectState() {
+        commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+
+        verify(mediaTttChipController).displayChip(any(TransferInitiated::class.java))
+    }
+
+    @Test
+    fun transferSucceeded_chipDisplayWithCorrectState() {
+        commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+
+        verify(mediaTttChipController).displayChip(any(TransferSucceeded::class.java))
+    }
+
+    @Test
+    fun removeCommand_chipRemoved() {
+        commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_TAG))
+
+        verify(mediaTttChipController).removeChip()
+    }
+
+    private fun getMoveCloserToTransferCommand(): Array<String> =
+        arrayOf(
+            ADD_CHIP_COMMAND_TAG,
+            DEVICE_NAME,
+            MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
+        )
+
+    private fun getTransferInitiatedCommand(): Array<String> =
+        arrayOf(
+            ADD_CHIP_COMMAND_TAG,
+            DEVICE_NAME,
+            TRANSFER_INITIATED_COMMAND_NAME
+        )
+
+    private fun getTransferSucceededCommand(): Array<String> =
+        arrayOf(
+            ADD_CHIP_COMMAND_TAG,
+            DEVICE_NAME,
+            TRANSFER_SUCCEEDED_COMMAND_NAME
+        )
+
+    class EmptyCommand : Command {
+        override fun execute(pw: PrintWriter, args: List<String>) {
+        }
+
+        override fun help(pw: PrintWriter) {
+        }
+    }
+}
+
+private const val DEVICE_NAME = "My Tablet"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 651bcde..8953788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -140,7 +140,7 @@
 
         mInternetDialog.updateDialog(true);
 
-        assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
new file mode 100644
index 0000000..c7c8d04
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
@@ -0,0 +1,340 @@
+/*
+ * 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;
+
+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.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.app.IForegroundServiceObserver;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.util.Pair;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.filters.MediumTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.RunningFgsController;
+import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime;
+import com.android.systemui.statusbar.policy.RunningFgsControllerImpl;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.function.Consumer;
+
+@MediumTest
+@RunWith(AndroidTestingRunner.class)
+public class RunningFgsControllerTest extends SysuiTestCase {
+
+    private RunningFgsController mController;
+
+    private FakeSystemClock mSystemClock = new FakeSystemClock();
+    private FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
+    private TestCallback mCallback = new TestCallback();
+
+    @Mock
+    private IActivityManager mActivityManager;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private LifecycleOwner mLifecycleOwner;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle);
+        mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager);
+    }
+
+    @Test
+    public void testInitRegistersListenerInImpl() throws RemoteException {
+        ((RunningFgsControllerImpl) mController).init();
+        verify(mActivityManager, times(1)).registerForegroundServiceObserver(any());
+    }
+
+    @Test
+    public void testAddCallbackCallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.addCallback(mCallback));
+    }
+
+    @Test
+    public void testRemoveCallbackCallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.removeCallback(mCallback));
+    }
+
+    @Test
+    public void testObserve1CallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback));
+    }
+
+    @Test
+    public void testObserve2CallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback));
+    }
+
+    @Test
+    public void testGetPackagesWithFgsCallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.getPackagesWithFgs());
+    }
+
+    @Test
+    public void testStopFgsCallsInitInImpl() {
+        verifyInitIsCalled(controller -> controller.stopFgs(0, ""));
+    }
+
+    /**
+     * Tests that callbacks can be added
+     */
+    @Test
+    public void testAddCallback() throws RemoteException {
+        String testPackageName = "testPackageName";
+        int testUserId = 0;
+
+        IForegroundServiceObserver observer = prepareObserver();
+        mController.addCallback(mCallback);
+
+        observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
+
+        mExecutor.advanceClockToLast();
+        mExecutor.runAllReady();
+
+        assertEquals("Callback should have been invoked exactly once.",
+                1, mCallback.mInvocations.size());
+
+        List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0);
+        assertEquals("There should have only been one package in callback. packages="
+                        + userPackageTimes,
+                1, userPackageTimes.size());
+
+        UserPackageTime upt = userPackageTimes.get(0);
+        assertEquals(testPackageName, upt.getPackageName());
+        assertEquals(testUserId, upt.getUserId());
+    }
+
+    /**
+     * Tests that callbacks can be removed. This test is only meaningful if
+     * {@link #testAddCallback()} can pass.
+     */
+    @Test
+    public void testRemoveCallback() throws RemoteException {
+        String testPackageName = "testPackageName";
+        int testUserId = 0;
+
+        IForegroundServiceObserver observer = prepareObserver();
+        mController.addCallback(mCallback);
+        mController.removeCallback(mCallback);
+
+        observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
+
+        mExecutor.advanceClockToLast();
+        mExecutor.runAllReady();
+
+        assertEquals("Callback should not have been invoked.",
+                0, mCallback.mInvocations.size());
+    }
+
+    /**
+     * Tests packages are added when the controller receives a callback from activity manager for
+     * a foreground service start.
+     */
+    @Test
+    public void testGetPackagesWithFgsAddingPackages() throws RemoteException {
+        int numPackages = 20;
+        int numUsers = 3;
+
+        IForegroundServiceObserver observer = prepareObserver();
+
+        assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size());
+
+        List<Pair<Integer, String>> addedPackages = new ArrayList<>();
+        for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
+            for (int userId = 0; userId < numUsers; userId++) {
+                String packageName = "package.name." + pkgNumber;
+                addedPackages.add(new Pair(userId, packageName));
+
+                observer.onForegroundStateChanged(new Binder(), packageName, userId, true);
+
+                containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs());
+            }
+        }
+    }
+
+    /**
+     * Tests packages are removed when the controller receives a callback from activity manager for
+     * a foreground service ending.
+     */
+    @Test
+    public void testGetPackagesWithFgsRemovingPackages() throws RemoteException {
+        int numPackages = 20;
+        int numUsers = 3;
+        int arrayLength = numPackages * numUsers;
+
+        String[] packages = new String[arrayLength];
+        int[] users = new int[arrayLength];
+        IBinder[] tokens = new IBinder[arrayLength];
+        for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
+            for (int userId = 0; userId < numUsers; userId++) {
+                int i = pkgNumber * numUsers + userId;
+                packages[i] =  "package.name." + pkgNumber;
+                users[i] = userId;
+                tokens[i] = new Binder();
+            }
+        }
+
+        IForegroundServiceObserver observer = prepareObserver();
+
+        for (int i = 0; i < packages.length; i++) {
+            observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true);
+        }
+
+        assertEquals(packages.length, mController.getPackagesWithFgs().size());
+
+        List<Integer> removeOrder = new ArrayList<>();
+        for (int i = 0; i < packages.length; i++) {
+            removeOrder.add(i);
+        }
+        Collections.shuffle(removeOrder, new Random(12345));
+
+        for (int idx : removeOrder) {
+            removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]);
+        }
+
+        assertEquals(0, mController.getPackagesWithFgs().size());
+    }
+
+    /**
+     * Tests a call on stopFgs forwards to activity manager.
+     */
+    @Test
+    public void testStopFgs() throws RemoteException {
+        String pkgName = "package.name";
+        mController.stopFgs(0, pkgName);
+        verify(mActivityManager).makeServicesNonForeground(pkgName, 0);
+    }
+
+    /**
+     * Tests a package which starts multiple services is only listed once and is only removed once
+     * all services are stopped.
+     */
+    @Test
+    public void testSinglePackageWithMultipleServices() throws RemoteException {
+        String packageName = "package.name";
+        int userId = 0;
+        IBinder serviceToken1 = new Binder();
+        IBinder serviceToken2 = new Binder();
+
+        IForegroundServiceObserver observer = prepareObserver();
+
+        assertEquals(0, mController.getPackagesWithFgs().size());
+
+        observer.onForegroundStateChanged(serviceToken1, packageName, userId, true);
+        assertSinglePackage(packageName, userId);
+
+        observer.onForegroundStateChanged(serviceToken2, packageName, userId, true);
+        assertSinglePackage(packageName, userId);
+
+        observer.onForegroundStateChanged(serviceToken2, packageName, userId, false);
+        assertSinglePackage(packageName, userId);
+
+        observer.onForegroundStateChanged(serviceToken1, packageName, userId, false);
+        assertEquals(0, mController.getPackagesWithFgs().size());
+    }
+
+    private IForegroundServiceObserver prepareObserver()
+            throws RemoteException {
+        mController.getPackagesWithFgs();
+
+        ArgumentCaptor<IForegroundServiceObserver> argumentCaptor =
+                ArgumentCaptor.forClass(IForegroundServiceObserver.class);
+        verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture());
+
+        return argumentCaptor.getValue();
+    }
+
+    private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) {
+        RunningFgsControllerImpl spiedController = Mockito.spy(
+                ((RunningFgsControllerImpl) mController));
+        c.accept(spiedController);
+        verify(spiedController, atLeastOnce()).init();
+    }
+
+    private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages,
+            List<UserPackageTime> runningFgsPackages) {
+        for (Pair<Integer, String> userPkg : addedPackages) {
+            assertTrue(userPkg + " was not found in returned list",
+                    runningFgsPackages.stream().anyMatch(
+                            upt -> userPkg.first == upt.getUserId()
+                                    && Objects.equals(upt.getPackageName(), userPkg.second)));
+        }
+        for (UserPackageTime upt : runningFgsPackages) {
+            int userId = upt.getUserId();
+            String packageName = upt.getPackageName();
+            assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">"
+                            + " in returned list",
+                    addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId
+                            && Objects.equals(packageName, userPkg.second)));
+        }
+    }
+
+    private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer,
+            IBinder token, String pkg, int userId) throws RemoteException {
+        observer.onForegroundStateChanged(token, pkg, userId, false);
+        List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs();
+        assertFalse("Package \"" + pkg + "\" was not removed",
+                packagesWithFgs.stream().anyMatch(upt ->
+                        Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId));
+    }
+
+    private void assertSinglePackage(String packageName, int userId) {
+        assertEquals(1, mController.getPackagesWithFgs().size());
+        assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName());
+        assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId());
+    }
+
+    private static class TestCallback implements RunningFgsController.Callback {
+
+        private List<List<UserPackageTime>> mInvocations = new ArrayList<>();
+
+        @Override
+        public void onFgsPackagesChanged(List<UserPackageTime> packages) {
+            mInvocations.add(packages);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index b02a336..ed8b532 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -37,6 +37,7 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.annotation.NonNull;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 
@@ -50,6 +51,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -78,6 +80,8 @@
             mock(StatusBarNotification.class);
 
     @Mock
+    DebugModeFilterProvider mDebugModeFilterProvider;
+    @Mock
     StatusBarStateController mStatusBarStateController;
     @Mock
     KeyguardEnvironment mEnvironment;
@@ -132,7 +136,13 @@
                 mDependency,
                 TestableLooper.get(this));
         mRow = testHelper.createRow();
-        mNotificationFilter = new NotificationFilter(
+        mNotificationFilter = newNotificationFilter();
+    }
+
+    @NonNull
+    private NotificationFilter newNotificationFilter() {
+        return new NotificationFilter(
+                mDebugModeFilterProvider,
                 mStatusBarStateController,
                 mEnvironment,
                 mFsc,
@@ -205,12 +215,7 @@
     public void shouldFilterOtherNotificationWhenDisabled() {
         // GIVEN that the media feature is disabled
         when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
-        NotificationFilter filter = new NotificationFilter(
-                mStatusBarStateController,
-                mEnvironment,
-                mFsc,
-                mUserManager,
-                mMediaFeatureFlag);
+        NotificationFilter filter = newNotificationFilter();
         // WHEN the media filter is asked about an entry
         NotificationEntry otherEntry = new NotificationEntryBuilder().build();
         final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
@@ -222,12 +227,7 @@
     public void shouldFilterOtherNotificationWhenEnabled() {
         // GIVEN that the media feature is enabled
         when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
-        NotificationFilter filter = new NotificationFilter(
-                mStatusBarStateController,
-                mEnvironment,
-                mFsc,
-                mUserManager,
-                mMediaFeatureFlag);
+        NotificationFilter filter = newNotificationFilter();
         // WHEN the media filter is asked about an entry
         NotificationEntry otherEntry = new NotificationEntryBuilder().build();
         final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
@@ -239,12 +239,7 @@
     public void shouldFilterMediaNotificationWhenDisabled() {
         // GIVEN that the media feature is disabled
         when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
-        NotificationFilter filter = new NotificationFilter(
-                mStatusBarStateController,
-                mEnvironment,
-                mFsc,
-                mUserManager,
-                mMediaFeatureFlag);
+        NotificationFilter filter = newNotificationFilter();
         // WHEN the media filter is asked about a media entry
         final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
         // THEN it shouldn't be filtered
@@ -255,12 +250,7 @@
     public void shouldFilterMediaNotificationWhenEnabled() {
         // GIVEN that the media feature is enabled
         when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
-        NotificationFilter filter = new NotificationFilter(
-                mStatusBarStateController,
-                mEnvironment,
-                mFsc,
-                mUserManager,
-                mMediaFeatureFlag);
+        NotificationFilter filter = newNotificationFilter();
         // WHEN the media filter is asked about a media entry
         final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
         // THEN it should be filtered
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 5b60c9e..ea68143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -4,6 +4,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.EmptyShadeView
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -55,4 +56,24 @@
         // top margin presence should decrease heads up translation up to minHeadsUpTranslation
         assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() {
+        stackScrollAlgorithm.initView(context)
+        val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
+            layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+        }
+        hostView.removeAllViews()
+        hostView.addView(emptyShadeView)
+        ambientState.layoutMaxHeight = 1280
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        val closeHandleUnderlapHeight =
+            context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
+        val fullHeight =
+            ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+        val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
+        assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java b/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
new file mode 100644
index 0000000..ff6fd6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.ref;
+
+import com.android.internal.util.GcUtils;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A WeakReference subclass that forces gc/finalizing on access.
+ */
+public class GcWeakReference<T> extends WeakReference<T> {
+    public GcWeakReference(T referent) {
+        super(referent);
+    }
+
+    @Override
+    public T get() throws RuntimeException {
+        GcUtils.runGcAndFinalizersSync();
+        return super.get();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 7c6d80b..22d7273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -99,7 +99,7 @@
     @Test
     public void testConnect() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, 0, mExecutor, mTransformer);
+                mIntent, mExecutor, mTransformer);
         // Register twice to ensure only one callback occurs.
         connection.addCallback(mCallback);
         connection.addCallback(mCallback);
@@ -119,7 +119,7 @@
     @Test
     public void testDisconnect() {
         ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
-                mIntent, 0, mExecutor, mTransformer);
+                mIntent, mExecutor, mTransformer);
         connection.addCallback(mCallback);
         connection.onServiceDisconnected(mComponentName);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index c35d51a..71ae5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -108,6 +108,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleData;
@@ -246,6 +247,8 @@
     private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock
     private AuthController mAuthController;
+    @Mock
+    private TaskViewTransitions mTaskViewTransitions;
 
     private TestableBubblePositioner mPositioner;
 
@@ -344,6 +347,7 @@
                 mock(DisplayController.class),
                 syncExecutor,
                 mock(Handler.class),
+                mTaskViewTransitions,
                 mock(SyncTransactionQueue.class));
         mBubbleController.setExpandListener(mBubbleExpandListener);
         spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index e2fce67..7b9e6a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -90,6 +90,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleData;
@@ -217,6 +218,8 @@
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private ScreenOffAnimationController mScreenOffAnimationController;
+    @Mock
+    private TaskViewTransitions mTaskViewTransitions;
 
     private TestableBubblePositioner mPositioner;
 
@@ -306,6 +309,7 @@
                 mock(DisplayController.class),
                 syncExecutor,
                 mock(Handler.class),
+                mTaskViewTransitions,
                 mock(SyncTransactionQueue.class));
         mBubbleController.setExpandListener(mBubbleExpandListener);
         spyOn(mBubbleController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 7b77cb0..80834c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -23,6 +23,7 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleData;
@@ -56,11 +57,12 @@
             DisplayController displayController,
             ShellExecutor shellMainExecutor,
             Handler shellMainHandler,
+            TaskViewTransitions taskViewTransitions,
             SyncTransactionQueue syncQueue) {
         super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
                 statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                 bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
-                shellMainExecutor, shellMainHandler, syncQueue);
+                shellMainExecutor, shellMainHandler, taskViewTransitions, syncQueue);
         setInflateSynchronously(true);
         initialize();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1e15d2a..2f2e536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -42,7 +43,6 @@
 import com.android.wm.shell.onehanded.OneHandedEventCallback;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
@@ -78,7 +78,7 @@
     @Mock WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock ProtoTracer mProtoTracer;
     @Mock ShellCommandHandler mShellCommandHandler;
-    @Mock SizeCompatUI mSizeCompatUI;
+    @Mock CompatUI mCompatUI;
     @Mock ShellExecutor mSysUiMainExecutor;
     @Mock DragAndDrop mDragAndDrop;
 
@@ -88,7 +88,7 @@
 
         mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
                 Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
-                Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
+                Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
                 Optional.of(mDragAndDrop),
                 mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
                 mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
@@ -136,8 +136,8 @@
     }
 
     @Test
-    public void initSizeCompatUI_registersCallbacks() {
-        mWMShell.initSizeCompatUi(mSizeCompatUI);
+    public void initCompatUI_registersCallbacks() {
+        mWMShell.initCompatUi(mCompatUI);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index eaf2694..6744ea8 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -288,8 +288,6 @@
                     showGlobalActions();
                     return true;
                 }
-                case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
-                    return toggleSplitScreen();
                 case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
                     return lockScreen();
                 case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
@@ -369,21 +367,6 @@
         mWindowManagerService.showGlobalActions();
     }
 
-    private boolean toggleSplitScreen() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            StatusBarManagerInternal statusBarService = LocalServices.getService(
-                    StatusBarManagerInternal.class);
-            if (statusBarService == null) {
-                return false;
-            }
-            statusBarService.toggleSplitScreen();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return true;
-    }
-
     private boolean lockScreen() {
         mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
                 PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b32d543..9c996f4 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -177,6 +177,7 @@
             new BluetoothDeviceConnectedListener();
     private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
     private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+    Set<Integer> mPresentSelfManagedDevices = new HashSet<>();
     private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
     private UnbindDeviceListenersRunnable
             mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
@@ -217,7 +218,7 @@
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
         mUserManager = context.getSystemService(UserManager.class);
-        mCompanionDevicePresenceController = new CompanionDevicePresenceController();
+        mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
         mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
 
         registerPackageMonitor();
@@ -555,6 +556,57 @@
             //TODO: b/199427116
         }
 
+        @Override
+        public void notifyDeviceAppeared(int associationId) {
+            final AssociationInfo association = getAssociationWithCallerChecks(associationId);
+            if (association == null) {
+                throw new IllegalArgumentException("Association with ID " + associationId + " "
+                        + "does not exist "
+                        + "or belongs to a different package "
+                        + "or belongs to a different user");
+            }
+
+            if (!association.isSelfManaged()) {
+                throw new IllegalArgumentException("Association with ID " + associationId
+                        + " is not self-managed. notifyDeviceAppeared(int) can only be called for"
+                        + " self-managed associations.");
+            }
+
+            if (!mPresentSelfManagedDevices.add(associationId)) {
+                Slog.w(LOG_TAG, "Association with ID " + associationId + " is already present");
+                return;
+            }
+
+            mCompanionDevicePresenceController.onDeviceNotifyAppeared(
+                    association, getContext(), mMainHandler);
+        }
+
+        @Override
+        public void notifyDeviceDisappeared(int associationId) {
+            final AssociationInfo association = getAssociationWithCallerChecks(associationId);
+            if (association == null) {
+                throw new IllegalArgumentException("Association with ID " + associationId + " "
+                        + "does not exist "
+                        + "or belongs to a different package "
+                        + "or belongs to a different user");
+            }
+
+            if (!association.isSelfManaged()) {
+                throw new IllegalArgumentException("Association with ID " + associationId
+                        + " is not self-managed. notifyDeviceAppeared(int) can only be called for"
+                        + " self-managed associations.");
+            }
+
+            if (!mPresentSelfManagedDevices.contains(associationId)) {
+                Slog.w(LOG_TAG, "Association with ID " + associationId + " is not connected");
+                return;
+            }
+
+            mPresentSelfManagedDevices.remove(associationId);
+            mCompanionDevicePresenceController.onDeviceNotifyDisappearedAndUnbind(
+                    association, getContext(), mMainHandler);
+        }
+
         private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
                 boolean active) throws RemoteException {
             getContext().enforceCallingOrSelfPermission(
@@ -645,11 +697,18 @@
                 }
 
             }
+
             fout.append("Currently Connected Devices:").append('\n');
             for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
                 fout.append("  ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
             }
 
+            fout.append("Currently SelfManaged Connected Devices associationId:").append('\n');
+            for (Integer associationId : mPresentSelfManagedDevices) {
+                fout.append("  ").append("AssociationId:  ").append(
+                        String.valueOf(associationId)).append('\n');
+            }
+
             fout.append("Devices Last Nearby:").append('\n');
             for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
                 String device = mDevicesLastNearby.keyAt(i);
@@ -774,7 +833,9 @@
     }
 
     void onAssociationPreRemove(AssociationInfo association) {
-        if (association.isNotifyOnDeviceNearby()) {
+        if (association.isNotifyOnDeviceNearby()
+                || (association.isSelfManaged()
+                && mPresentSelfManagedDevices.contains(association.getId()))) {
             mCompanionDevicePresenceController.unbindDevicePresenceListener(
                     association.getPackageName(), association.getUserId());
         }
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 3e00846..4447684 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -49,8 +49,10 @@
     private static final String LOG_TAG = "CompanionDevicePresenceController";
     PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
     private static final String META_DATA_KEY_PRIMARY = "primary";
+    private final CompanionDeviceManagerService mService;
 
-    public CompanionDevicePresenceController() {
+    public CompanionDevicePresenceController(CompanionDeviceManagerService service) {
+        mService = service;
         mBoundServices = new PerUser<ArrayMap<String, List<BoundService>>>() {
             @NonNull
             @Override
@@ -61,24 +63,45 @@
     }
 
     void onDeviceNotifyAppeared(AssociationInfo association, Context context, Handler handler) {
-        ServiceConnector<ICompanionDeviceService> primaryConnector =
-                getPrimaryServiceConnector(association, context, handler);
-        if (primaryConnector != null) {
-            Slog.i(LOG_TAG,
-                    "Sending onDeviceAppeared to " + association.getPackageName() + ")");
-            primaryConnector.run(
-                    s -> s.onDeviceAppeared(association.getDeviceMacAddressAsString()));
+        for (BoundService boundService : getDeviceListenerServiceConnector(
+                association, context,  handler)) {
+            if (boundService.mIsPrimary) {
+                Slog.i(LOG_TAG,
+                        "Sending onDeviceAppeared to " + association.getPackageName() + ")");
+                boundService.mServiceConnector.run(
+                        service -> service.onDeviceAppeared(association));
+            } else {
+                Slog.i(LOG_TAG, "Connecting to " + boundService.mComponentName);
+                boundService.mServiceConnector.connect();
+            }
         }
     }
 
     void onDeviceNotifyDisappeared(AssociationInfo association, Context context, Handler handler) {
-        ServiceConnector<ICompanionDeviceService> primaryConnector =
-                getPrimaryServiceConnector(association, context, handler);
-        if (primaryConnector != null) {
-            Slog.i(LOG_TAG,
-                    "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
-            primaryConnector.run(
-                    s -> s.onDeviceDisappeared(association.getDeviceMacAddressAsString()));
+        for (BoundService boundService : getDeviceListenerServiceConnector(
+                association, context,  handler)) {
+            if (boundService.mIsPrimary) {
+                Slog.i(LOG_TAG,
+                        "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
+                boundService.mServiceConnector.run(service ->
+                        service.onDeviceDisappeared(association));
+            }
+        }
+    }
+
+    void onDeviceNotifyDisappearedAndUnbind(AssociationInfo association,
+            Context context, Handler handler) {
+        for (BoundService boundService : getDeviceListenerServiceConnector(
+                association, context, handler)) {
+            if (boundService.mIsPrimary) {
+                Slog.i(LOG_TAG,
+                        "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
+                boundService.mServiceConnector.post(
+                        service -> {
+                            service.onDeviceDisappeared(association);
+                        }).thenRun(() -> unbindDevicePresenceListener(
+                                        association.getPackageName(), association.getUserId()));
+            }
         }
     }
 
@@ -93,17 +116,6 @@
         }
     }
 
-    private ServiceConnector<ICompanionDeviceService> getPrimaryServiceConnector(
-            AssociationInfo association, Context context, Handler handler) {
-        for (BoundService boundService: getDeviceListenerServiceConnector(association, context,
-                handler)) {
-            if (boundService.mIsPrimary) {
-                return boundService.mServiceConnector;
-            }
-        }
-        return null;
-    }
-
     private List<BoundService> getDeviceListenerServiceConnector(AssociationInfo a, Context context,
             Handler handler) {
         return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
@@ -140,18 +152,22 @@
                         protected long getAutoDisconnectTimeoutMs() {
                             // Service binding is managed manually based on corresponding device
                             // being nearby
-                            return Long.MAX_VALUE;
+                            return -1;
                         }
 
                         @Override
                         public void binderDied() {
                             super.binderDied();
-
-                            // Re-connect to the service if process gets killed
-                            handler.postDelayed(
-                                    this::connect,
-                                    CompanionDeviceManagerService
-                                            .DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+                            if (a.isSelfManaged()) {
+                                mBoundServices.forUser(a.getUserId()).remove(a.getPackageName());
+                                mService.mPresentSelfManagedDevices.remove(a.getId());
+                            } else {
+                                // Re-connect to the service if process gets killed
+                                handler.postDelayed(
+                                        this::connect,
+                                        CompanionDeviceManagerService
+                                                .DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+                            }
                         }
                     };
 
@@ -191,6 +207,13 @@
             }
         }
 
+        if (packageResolveInfos.size() > 1 && primaryCount == 0) {
+            Slog.e(LOG_TAG, "Must have exactly one primary CompanionDeviceService "
+                    + "to be bound when declare more than one CompanionDeviceService but "
+                    + association.getPackageName() + " has " + primaryCount);
+            return false;
+        }
+
         if (packageResolveInfos.size() == 1 && primaryCount != 0) {
             Slog.w(LOG_TAG, "Do not need the primary metadata if there's only one"
                     + " CompanionDeviceService " + "but " + association.getPackageName()
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
new file mode 100644
index 0000000..067edcc
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -0,0 +1,283 @@
+/*
+ * 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.companion.virtual;
+
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.Map;
+
+/** Controls virtual input devices, including device lifecycle and event dispatch. */
+class InputController {
+
+    private final Object mLock;
+
+    /* Token -> file descriptor associations. */
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    final Map<IBinder, Integer> mInputDeviceFds = new ArrayMap<>();
+
+    private final NativeWrapper mNativeWrapper;
+
+    InputController(@NonNull Object lock) {
+        this(lock, new NativeWrapper());
+    }
+
+    @VisibleForTesting
+    InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
+        mLock = lock;
+        mNativeWrapper = nativeWrapper;
+    }
+
+    void close() {
+        synchronized (mLock) {
+            for (int fd : mInputDeviceFds.values()) {
+                mNativeWrapper.closeUinput(fd);
+            }
+            mInputDeviceFds.clear();
+        }
+    }
+
+    void createKeyboard(@NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken) {
+        final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
+        if (fd < 0) {
+            throw new RuntimeException(
+                    "A native error occurred when creating keyboard: " + -fd);
+        }
+        synchronized (mLock) {
+            mInputDeviceFds.put(deviceToken, fd);
+        }
+        try {
+            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not create virtual keyboard", e);
+        }
+    }
+
+    void createMouse(@NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken) {
+        final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
+        if (fd < 0) {
+            throw new RuntimeException(
+                    "A native error occurred when creating mouse: " + -fd);
+        }
+        synchronized (mLock) {
+            mInputDeviceFds.put(deviceToken, fd);
+        }
+        try {
+            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not create virtual mouse", e);
+        }
+    }
+
+    void createTouchscreen(@NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken,
+            @NonNull Point screenSize) {
+        final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
+                screenSize.y, screenSize.x);
+        if (fd < 0) {
+            throw new RuntimeException(
+                    "A native error occurred when creating touchscreen: " + -fd);
+        }
+        synchronized (mLock) {
+            mInputDeviceFds.put(deviceToken, fd);
+        }
+        try {
+            deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not create virtual touchscreen", e);
+        }
+    }
+
+    void unregisterInputDevice(@NonNull IBinder token) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.remove(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not unregister input device for given token");
+            }
+            mNativeWrapper.closeUinput(fd);
+        }
+    }
+
+    boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.get(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not send key event to input device for given token");
+            }
+            return mNativeWrapper.writeKeyEvent(fd, event.getKeyCode(), event.getAction());
+        }
+    }
+
+    boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.get(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not send button event to input device for given token");
+            }
+            return mNativeWrapper.writeButtonEvent(fd, event.getButtonCode(), event.getAction());
+        }
+    }
+
+    boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.get(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not send touch event to input device for given token");
+            }
+            return mNativeWrapper.writeTouchEvent(fd, event.getPointerId(), event.getToolType(),
+                    event.getAction(), event.getX(), event.getY(), event.getPressure(),
+                    event.getMajorAxisSize());
+        }
+    }
+
+    boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.get(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not send relative event to input device for given token");
+            }
+            return mNativeWrapper.writeRelativeEvent(fd, event.getRelativeX(),
+                    event.getRelativeY());
+        }
+    }
+
+    boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) {
+        synchronized (mLock) {
+            final Integer fd = mInputDeviceFds.get(token);
+            if (fd == null) {
+                throw new IllegalArgumentException(
+                        "Could not send scroll event to input device for given token");
+            }
+            return mNativeWrapper.writeScrollEvent(fd, event.getXAxisMovement(),
+                    event.getYAxisMovement());
+        }
+    }
+
+    public void dump(@NonNull PrintWriter fout) {
+        fout.println("    InputController: ");
+        synchronized (mLock) {
+            fout.println("      Active file descriptors: ");
+            for (int inputDeviceFd : mInputDeviceFds.values()) {
+                fout.println(inputDeviceFd);
+            }
+        }
+    }
+
+    private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
+            int productId);
+    private static native int nativeOpenUinputMouse(String deviceName, int vendorId,
+            int productId);
+    private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
+            int productId, int height, int width);
+    private static native boolean nativeCloseUinput(int fd);
+    private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
+    private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
+    private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
+            int action, float locationX, float locationY, float pressure, float majorAxisSize);
+    private static native boolean nativeWriteRelativeEvent(int fd, float relativeX,
+            float relativeY);
+    private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement,
+            float yAxisMovement);
+
+    /** Wrapper around the static native methods for tests. */
+    @VisibleForTesting
+    protected static class NativeWrapper {
+        public int openUinputKeyboard(String deviceName, int vendorId, int productId) {
+            return nativeOpenUinputKeyboard(deviceName, vendorId,
+                    productId);
+        }
+
+        public int openUinputMouse(String deviceName, int vendorId, int productId) {
+            return nativeOpenUinputMouse(deviceName, vendorId,
+                    productId);
+        }
+
+        public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height,
+                int width) {
+            return nativeOpenUinputTouchscreen(deviceName, vendorId,
+                    productId, height, width);
+        }
+
+        public boolean closeUinput(int fd) {
+            return nativeCloseUinput(fd);
+        }
+
+        public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
+            return nativeWriteKeyEvent(fd, androidKeyCode, action);
+        }
+
+        public boolean writeButtonEvent(int fd, int buttonCode, int action) {
+            return nativeWriteButtonEvent(fd, buttonCode, action);
+        }
+
+        public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action,
+                float locationX, float locationY, float pressure, float majorAxisSize) {
+            return nativeWriteTouchEvent(fd, pointerId, toolType,
+                    action, locationX, locationY,
+                    pressure, majorAxisSize);
+        }
+
+        public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) {
+            return nativeWriteRelativeEvent(fd, relativeX, relativeY);
+        }
+
+        public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) {
+            return nativeWriteScrollEvent(fd, xAxisMovement,
+                    yAxisMovement);
+        }
+    }
+
+    private final class BinderDeathRecipient implements IBinder.DeathRecipient {
+
+        private final IBinder mDeviceToken;
+
+        BinderDeathRecipient(IBinder deviceToken) {
+            mDeviceToken = deviceToken;
+        }
+
+        @Override
+        public void binderDied() {
+            unregisterInputDevice(mDeviceToken);
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
new file mode 100644
index 0000000..022da43
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -0,0 +1,278 @@
+/*
+ * 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.companion.virtual;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.annotation.NonNull;
+import android.companion.AssociationInfo;
+import android.companion.virtual.IVirtualDevice;
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.DisplayWindowPolicyController;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+
+final class VirtualDeviceImpl extends IVirtualDevice.Stub
+        implements IBinder.DeathRecipient {
+
+    private final Object mVirtualDeviceLock = new Object();
+
+    private final Context mContext;
+    private final AssociationInfo mAssociationInfo;
+    private final int mOwnerUid;
+    private final GenericWindowPolicyController mGenericWindowPolicyController;
+    private final InputController mInputController;
+    @VisibleForTesting
+    final List<Integer> mVirtualDisplayIds = new ArrayList<>();
+    private final OnDeviceCloseListener mListener;
+
+    VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
+            IBinder token, int ownerUid, OnDeviceCloseListener listener) {
+        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
+    }
+
+    @VisibleForTesting
+    VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
+            int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
+        mContext = context;
+        mAssociationInfo = associationInfo;
+        mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
+                SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        mOwnerUid = ownerUid;
+        if (inputController == null) {
+            mInputController = new InputController(mVirtualDeviceLock);
+        } else {
+            mInputController = inputController;
+        }
+        mListener = listener;
+        try {
+            token.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public int getAssociationId() {
+        return mAssociationInfo.getId();
+    }
+
+    @Override // Binder call
+    public void close() {
+        mListener.onClose(mAssociationInfo.getId());
+        mInputController.close();
+    }
+
+    @Override
+    public void binderDied() {
+        close();
+    }
+
+    @Override // Binder call
+    public void createVirtualKeyboard(
+            int displayId,
+            @NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+                "Permission required to create a virtual keyboard");
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplayIds.contains(displayId)) {
+                throw new SecurityException(
+                        "Cannot create a virtual keyboard for a display not associated with "
+                                + "this virtual device");
+            }
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override // Binder call
+    public void createVirtualMouse(
+            int displayId,
+            @NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+                "Permission required to create a virtual mouse");
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplayIds.contains(displayId)) {
+                throw new SecurityException(
+                        "Cannot create a virtual mouse for a display not associated with this "
+                                + "virtual device");
+            }
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mInputController.createMouse(deviceName, vendorId, productId, deviceToken);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override // Binder call
+    public void createVirtualTouchscreen(
+            int displayId,
+            @NonNull String deviceName,
+            int vendorId,
+            int productId,
+            @NonNull IBinder deviceToken,
+            @NonNull Point screenSize) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+                "Permission required to create a virtual touchscreen");
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplayIds.contains(displayId)) {
+                throw new SecurityException(
+                        "Cannot create a virtual touchscreen for a display not associated with "
+                                + "this virtual device");
+            }
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mInputController.createTouchscreen(deviceName, vendorId, productId,
+                    deviceToken, screenSize);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override // Binder call
+    public void unregisterInputDevice(IBinder token) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+                "Permission required to unregister this input device");
+
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            mInputController.unregisterInputDevice(token);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override // Binder call
+    public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.sendKeyEvent(token, event);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override // Binder call
+    public boolean sendButtonEvent(IBinder token, VirtualMouseButtonEvent event) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.sendButtonEvent(token, event);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override // Binder call
+    public boolean sendTouchEvent(IBinder token, VirtualTouchEvent event) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.sendTouchEvent(token, event);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override // Binder call
+    public boolean sendRelativeEvent(IBinder token, VirtualMouseRelativeEvent event) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.sendRelativeEvent(token, event);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override // Binder call
+    public boolean sendScrollEvent(IBinder token, VirtualMouseScrollEvent event) {
+        final long binderToken = Binder.clearCallingIdentity();
+        try {
+            return mInputController.sendScrollEvent(token, event);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        fout.println("  VirtualDevice: ");
+        fout.println("    mVirtualDisplayIds: ");
+        synchronized (mVirtualDeviceLock) {
+            for (int id : mVirtualDisplayIds) {
+                fout.println("      " + id);
+            }
+        }
+        mInputController.dump(fout);
+    }
+
+    DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
+        if (mVirtualDisplayIds.contains(displayId)) {
+            throw new IllegalStateException(
+                    "Virtual device already have a virtual display with ID " + displayId);
+        }
+        mVirtualDisplayIds.add(displayId);
+        return mGenericWindowPolicyController;
+    }
+
+    void onVirtualDisplayRemovedLocked(int displayId) {
+        if (!mVirtualDisplayIds.contains(displayId)) {
+            throw new IllegalStateException(
+                    "Virtual device doesn't have a virtual display with ID " + displayId);
+        }
+        mVirtualDisplayIds.remove(displayId);
+    }
+
+    int getOwnerUid() {
+        return mOwnerUid;
+    }
+
+    interface OnDeviceCloseListener {
+        void onClose(int associationId);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 2742608..46e75f7 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,9 +16,6 @@
 
 package com.android.server.companion.virtual;
 
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -42,7 +39,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -52,6 +48,7 @@
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "VirtualDeviceManagerService";
+
     private final Object mVirtualDeviceManagerLock = new Object();
     private final VirtualDeviceManagerImpl mImpl;
 
@@ -130,64 +127,9 @@
         }
     }
 
-    private class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient {
-
-        private final AssociationInfo mAssociationInfo;
-        private final int mOwnerUid;
-        private final GenericWindowPolicyController mGenericWindowPolicyController;
-        private final ArrayList<Integer> mDisplayIds = new ArrayList<>();
-
-        private VirtualDeviceImpl(int ownerUid, IBinder token, AssociationInfo associationInfo) {
-            mOwnerUid = ownerUid;
-            mAssociationInfo = associationInfo;
-            mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
-                    SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-            try {
-                token.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-            mVirtualDevices.put(associationInfo.getId(), this);
-        }
-
-        @Override
-        public int getAssociationId() {
-            return mAssociationInfo.getId();
-        }
-
-        @Override
-        public void close() {
-            synchronized (mVirtualDeviceManagerLock) {
-                mVirtualDevices.remove(mAssociationInfo.getId());
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            close();
-        }
-
-        DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
-            if (mDisplayIds.contains(displayId)) {
-                throw new IllegalStateException(
-                        "Virtual device already have a virtual display with ID " + displayId);
-            }
-            mDisplayIds.add(displayId);
-            return mGenericWindowPolicyController;
-        }
-
-        void onVirtualDisplayRemovedLocked(int displayId) {
-            if (!mDisplayIds.contains(displayId)) {
-                throw new IllegalStateException(
-                        "Virtual device doesn't have a virtual display with ID " + displayId);
-            }
-            mDisplayIds.remove(displayId);
-        }
-    }
-
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
 
-        @Override
+        @Override // Binder call
         public IVirtualDevice createVirtualDevice(
                 IBinder token, String packageName, int associationId) {
             getContext().enforceCallingOrSelfPermission(
@@ -209,7 +151,18 @@
                             "Virtual device for association ID " + associationId
                                     + " already exists");
                 }
-                return new VirtualDeviceImpl(callingUid, token, associationInfo);
+                VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
+                        associationInfo, token, callingUid,
+                        new VirtualDeviceImpl.OnDeviceCloseListener() {
+                            @Override
+                            public void onClose(int associationId) {
+                                synchronized (mVirtualDeviceManagerLock) {
+                                    mVirtualDevices.remove(associationId);
+                                }
+                            }
+                        });
+                mVirtualDevices.put(associationInfo.getId(), virtualDevice);
+                return virtualDevice;
             }
         }
 
@@ -254,8 +207,7 @@
             fout.println("Created virtual devices: ");
             synchronized (mVirtualDeviceManagerLock) {
                 for (int i = 0; i < mVirtualDevices.size(); i++) {
-                    VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
-                    fout.printf("%d: %s\n", mVirtualDevices.keyAt(i), virtualDevice);
+                    mVirtualDevices.valueAt(i).dump(fd, fout, args);
                 }
             }
         }
@@ -290,7 +242,7 @@
             synchronized (mVirtualDeviceManagerLock) {
                 int size = mVirtualDevices.size();
                 for (int i = 0; i < size; i++) {
-                    if (mVirtualDevices.valueAt(i).mOwnerUid == uid) {
+                    if (mVirtualDevices.valueAt(i).getOwnerUid() == uid) {
                         return true;
                     }
                 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ba6854b..9641eb5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -121,7 +121,7 @@
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
-        ":services.connectivity-nsd-sources",
+        ":services.connectivity-tiramisu-sources",
     ],
 
     libs: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 121a0bc..6fe2806 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,10 +28,6 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.ComponentInfoFlags;
-import android.content.pm.PackageManager.PackageInfoFlags;
-import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.content.pm.SigningDetails.CertCapabilities;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.component.ParsedMainComponent;
@@ -199,7 +195,7 @@
      * @see PackageManager#getPackageInfo(String, int)
      */
     public abstract PackageInfo getPackageInfo(String packageName,
-            @PackageInfoFlags long flags, int filterCallingUid, int userId);
+            @PackageManager.PackageInfoFlagsBits long flags, int filterCallingUid, int userId);
 
     /**
      * Retrieve CE data directory inode number of an application.
@@ -228,7 +224,8 @@
      *         deleted with {@code DELETE_KEEP_DATA} flag set).
      */
     public abstract List<ApplicationInfo> getInstalledApplications(
-            @ApplicationInfoFlags long flags, @UserIdInt int userId, int callingUid);
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
+            int callingUid);
 
     /**
      * Retrieve launcher extras for a suspended package provided to the system in
@@ -325,7 +322,8 @@
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
      */
-    public abstract int getPackageUid(String packageName, @PackageInfoFlags long flags, int userId);
+    public abstract int getPackageUid(String packageName,
+            @PackageManager.PackageInfoFlagsBits long flags, int userId);
 
     /**
      * Retrieve all of the information we know about a particular package/application.
@@ -334,7 +332,7 @@
      * @see PackageManager#getApplicationInfo(String, int)
      */
     public abstract ApplicationInfo getApplicationInfo(String packageName,
-            @ApplicationInfoFlags long flags, int filterCallingUid, int userId);
+            @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId);
 
     /**
      * Retrieve all of the information we know about a particular activity class.
@@ -343,7 +341,7 @@
      * @see PackageManager#getActivityInfo(ComponentName, int)
      */
     public abstract ActivityInfo getActivityInfo(ComponentName component,
-            @ComponentInfoFlags long flags, int filterCallingUid, int userId);
+            @PackageManager.ComponentInfoFlagsBits long flags, int filterCallingUid, int userId);
 
     /**
      * Retrieve all activities that can be performed for the given intent.
@@ -354,22 +352,24 @@
      * @see PackageManager#queryIntentActivities(Intent, int)
      */
     public abstract List<ResolveInfo> queryIntentActivities(
-            Intent intent, @Nullable String resolvedType, @ResolveInfoFlags long flags,
-            int filterCallingUid, int userId);
+            Intent intent, @Nullable String resolvedType,
+            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId);
 
 
     /**
      * Retrieve all receivers that can handle a broadcast of the given intent.
      */
     public abstract List<ResolveInfo> queryIntentReceivers(Intent intent,
-            String resolvedType, @ResolveInfoFlags long flags, int filterCallingUid, int userId);
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
+            int filterCallingUid, int userId);
 
     /**
      * Retrieve all services that can be performed for the given intent.
      * @see PackageManager#queryIntentServices(Intent, int)
      */
     public abstract List<ResolveInfo> queryIntentServices(
-            Intent intent, @ResolveInfoFlags long flags, int callingUid, int userId);
+            Intent intent, @PackageManager.ResolveInfoFlagsBits long flags, int callingUid,
+            int userId);
 
     /**
      * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
@@ -593,20 +593,21 @@
      * Resolves an activity intent, allowing instant apps to be resolved.
      */
     public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
-            @ResolveInfoFlags long flags, @PrivateResolveFlags long privateResolveFlags, int userId,
-            boolean resolveForStart, int filterCallingUid);
+            @PackageManager.ResolveInfoFlagsBits long flags,
+            @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
+            int filterCallingUid);
 
     /**
     * Resolves a service intent, allowing instant apps to be resolved.
     */
     public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
-            @ResolveInfoFlags long flags, int userId, int callingUid);
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid);
 
     /**
     * Resolves a content provider intent.
     */
-    public abstract ProviderInfo resolveContentProvider(String name, @ComponentInfoFlags long flags,
-            int userId, int callingUid);
+    public abstract ProviderInfo resolveContentProvider(String name,
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId, int callingUid);
 
     /**
      * Track the creator of a new isolated uid.
@@ -878,7 +879,7 @@
 
     /** Returns {@code true} if the specified component is enabled and matches the given flags. */
     public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component,
-            @ComponentInfoFlags long flags, int userId);
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId);
 
     /** Returns {@code true} if the given user requires extra badging for icons. */
     public abstract boolean userNeedsBadging(int userId);
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 263ff18..380b1f3 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -112,9 +112,9 @@
     void handleAirplaneModeChange() {
         if (shouldSkipAirplaneModeChange()) {
             Log.i(TAG, "Ignore airplane mode change");
-            // We have to store Bluetooth state here, so if user turns off Bluetooth
-            // after airplane mode is turned on, we don't forget to turn on Bluetooth
-            // when airplane mode turns off.
+            // Airplane mode enabled when Bluetooth is being used for audio/headering aid.
+            // Bluetooth is not disabled in such case, only state is changed to
+            // BLUETOOTH_ON_AIRPLANE mode.
             mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
                     BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
             if (shouldPopToast()) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 8860a81..450e988 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1850,6 +1850,10 @@
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     mEnable = true;
 
+                    if (isBle == 0) {
+                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+                    }
+
                     // Use service interface to get the exact state
                     try {
                         mBluetoothLock.readLock().lock();
@@ -1863,7 +1867,6 @@
                                     } else {
                                         Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
                                         mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                                     }
                                     break;
                                 case BluetoothAdapter.STATE_BLE_TURNING_ON:
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4775127..91cd2f6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -152,6 +152,8 @@
 
         int callerUid;
         int callerPid;
+        boolean renounceFineLocationAccess;
+        boolean renounceCoarseLocationAccess;
 
         Set<Integer> eventList;
 
@@ -995,14 +997,25 @@
     }
 
     @Override
-    public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
-            IPhoneStateListener callback, int[] events, boolean notifyNow) {
+    public void listenWithEventList(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess, int subId, String callingPackage,
+            String callingFeatureId, IPhoneStateListener callback,
+            int[] events, boolean notifyNow) {
         Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
-        listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
+        listen(renounceFineLocationAccess, renounceFineLocationAccess, callingPackage,
+                callingFeatureId, callback, eventList, notifyNow, subId);
     }
 
     private void listen(String callingPackage, @Nullable String callingFeatureId,
             IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {
+        listen(false, false, callingPackage,
+                callingFeatureId, callback, events, notifyNow, subId);
+    }
+
+    private void listen(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess, String callingPackage,
+            @Nullable String callingFeatureId, IPhoneStateListener callback,
+            Set<Integer> events, boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
@@ -1047,6 +1060,8 @@
             r.callback = callback;
             r.callingPackage = callingPackage;
             r.callingFeatureId = callingFeatureId;
+            r.renounceCoarseLocationAccess = renounceCoarseLocationAccess;
+            r.renounceFineLocationAccess = renounceFineLocationAccess;
             r.callerUid = Binder.getCallingUid();
             r.callerPid = Binder.getCallingPid();
             // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
@@ -3199,6 +3214,9 @@
      * If you don't need app compat logic, use {@link #checkFineLocationAccess(Record)}.
      */
     private boolean checkFineLocationAccess(Record r, int minSdk) {
+        if (r.renounceFineLocationAccess) {
+            return false;
+        }
         LocationAccessPolicy.LocationPermissionQuery query =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(r.callingPackage)
@@ -3225,6 +3243,9 @@
      * If you don't need app compat logic, use {@link #checkCoarseLocationAccess(Record)}.
      */
     private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+        if (r.renounceCoarseLocationAccess) {
+            return false;
+        }
         LocationAccessPolicy.LocationPermissionQuery query =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(r.callingPackage)
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1a89ae7..2def50e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -371,6 +371,16 @@
     private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
 
     /**
+     * Native heap allocations in AppZygote process and its descendants will now have a
+     * non-zero tag in the most significant byte.
+     * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+     * Pointers</a>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677;
+
+    /**
      * Enable asynchronous (ASYNC) memory tag checking in this process. This
      * flag will only have an effect on hardware supporting the ARM Memory
      * Tagging Extension (MTE).
@@ -1741,6 +1751,16 @@
         return level;
     }
 
+    private int decideTaggingLevelForAppZygote(ProcessRecord app) {
+        int level = decideTaggingLevel(app);
+        // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
+        if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info)
+                && level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+            level = Zygote.MEMORY_TAG_LEVEL_NONE;
+        }
+        return level;
+    }
+
     private int decideGwpAsanLevel(ProcessRecord app) {
         // Look at the process attribute first.
        if (app.processInfo != null
@@ -2271,7 +2291,7 @@
                 // not the calling one.
                 appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
                 appInfo.uid = uid;
-                int runtimeFlags = decideTaggingLevel(app);
+                int runtimeFlags = decideTaggingLevelForAppZygote(app);
                 appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags);
                 mAppZygotes.put(app.info.processName, uid, appZygote);
                 zygoteProcessList = new ArrayList<ProcessRecord>();
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index e4ac7be..2fcdd61 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -327,7 +327,7 @@
         }
 
         boolean isBtScoRequested = isBluetoothScoRequested();
-        if (isBtScoRequested && !wasBtScoRequested) {
+        if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
             if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
                 Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
                         + pid);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index aa33644..6238198 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8252,6 +8252,9 @@
     public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName,
             String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
+        if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) != 0) {
+            throw new IllegalArgumentException("Invalid test flag");
+        }
         final int uid = Binder.getCallingUid();
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
                 .setUid(uid)
@@ -8310,7 +8313,7 @@
     /** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
     public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName,
-            int fakeUid, int sdk) {
+            int flags, int fakeUid, int sdk) {
         if (!enforceQueryAudioStateForTest("focus request")) {
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
@@ -8320,7 +8323,7 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
+                clientId, callingPackageName, null, flags,
                 sdk, false /*forceDuck*/, fakeUid);
     }
 
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index bf49ac8..66f6235 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -461,13 +461,19 @@
      *                 at the top of the focus stack
      * Push the focus requester onto the audio focus stack at the first position immediately
      * following the locked focus owners.
+     * Propagate through the stack the changes that the new (future) focus owner causes.
+     * @param nfr the future focus owner that will gain focus when the locked focus owners are
+     *            removed from the stack
      * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
      *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
      */
     @GuardedBy("mAudioFocusLock")
-    private int pushBelowLockedFocusOwners(FocusRequester nfr) {
+    private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) {
+        if (DEBUG) {
+            Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId());
+        }
         int lastLockedFocusOwnerIndex = mFocusStack.size();
-        for (int index = mFocusStack.size()-1; index >= 0; index--) {
+        for (int index = mFocusStack.size() - 1; index >= 0; index--) {
             if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
                 lastLockedFocusOwnerIndex = index;
             }
@@ -480,10 +486,33 @@
             propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
             mFocusStack.push(nfr);
             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
-        } else {
-            mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
-            return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
         }
+
+        if (DEBUG) {
+            Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex);
+        }
+        mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
+
+        // propagate potential focus loss (and removal from stack) after the newly
+        // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1)
+        final List<String> clientsToRemove = new LinkedList<String>();
+        for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) {
+            final boolean isDefinitiveLoss =
+                    mFocusStack.elementAt(index).handleFocusLossFromGain(
+                            nfr.getGainRequest(), nfr, false /*forceDuck*/);
+            if (isDefinitiveLoss) {
+                clientsToRemove.add(mFocusStack.elementAt(index).getClientId());
+            }
+        }
+        for (String clientToRemove : clientsToRemove) {
+            if (DEBUG) {
+                Log.v(TAG, "> removing focus client " + clientToRemove);
+            }
+            removeFocusStackEntry(clientToRemove, false /*signal*/,
+                    true /*notifyFocusFollowers*/);
+        }
+
+        return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
     }
 
     /**
@@ -868,7 +897,7 @@
      * @param forceDuck only true if
      *     {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
      *                  accessibility.
-     * @param testUid ignored if flags is not AudioManager.AUDIOFOCUS_FLAG_TEST (strictly equals to)
+     * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
      *                otherwise the UID being injected for testing
      * @return
      */
@@ -1029,7 +1058,7 @@
             if (focusGrantDelayed) {
                 // focusGrantDelayed being true implies we can't reassign focus right now
                 // which implies the focus stack is not empty.
-                final int requestResult = pushBelowLockedFocusOwners(nfr);
+                final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr);
                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
                 }
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 5ce72c2..3120dc5 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.compat.CompatChanges;
-import android.app.TaskStackListener;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.Overridable;
@@ -35,6 +34,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.hardware.CameraSessionStats;
 import android.hardware.CameraStreamStats;
@@ -84,7 +84,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -309,8 +308,6 @@
 
     private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener();
 
-    private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
-
     public static final class TaskInfo {
         public int frontTaskId;
         public boolean isResizeable;
@@ -320,54 +317,6 @@
         public int userId;
     }
 
-    private final class TaskStateHandler extends TaskStackListener {
-        private final Object mMapLock = new Object();
-
-        // maps the package name to its corresponding current top level task id
-        @GuardedBy("mMapLock")
-        private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
-
-        @Override
-        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
-            synchronized (mMapLock) {
-                TaskInfo info = new TaskInfo();
-                info.frontTaskId = taskInfo.taskId;
-                info.isResizeable =
-                        (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
-                info.displayId = taskInfo.displayId;
-                info.userId = taskInfo.userId;
-                info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
-                        taskInfo.topActivityInfo.screenOrientation);
-                info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
-                        taskInfo.topActivityInfo.screenOrientation);
-                mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
-            }
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            synchronized (mMapLock) {
-                for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
-                    if (entry.getValue().frontTaskId == taskId) {
-                        mTaskInfoMap.remove(entry.getKey());
-                        break;
-                    }
-                }
-            }
-        }
-
-        public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
-            synchronized (mMapLock) {
-                if (mTaskInfoMap.containsKey(packageName)) {
-                    return mTaskInfoMap.get(packageName);
-                }
-            }
-
-            Log.e(TAG, "Top task with package name: " + packageName + " not found!");
-            return null;
-        }
-    }
-
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -490,18 +439,53 @@
 
     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
         @Override
-        public int getRotateAndCropOverride(String packageName, int lensFacing) {
+        public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
                         " camera service UID!");
                 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
             }
 
+            TaskInfo taskInfo = null;
+            ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null;
+
+            try {
+                recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1,
+                        /*flags*/ 0, userId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to query recent tasks!");
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+            }
+
+            if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
+                ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0);
+                if (packageName.equals(task.topActivityInfo.packageName)) {
+                    taskInfo = new TaskInfo();
+                    taskInfo.frontTaskId = task.taskId;
+                    taskInfo.isResizeable =
+                            (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
+                    taskInfo.displayId = task.displayId;
+                    taskInfo.userId = task.userId;
+                    taskInfo.isFixedOrientationLandscape =
+                            ActivityInfo.isFixedOrientationLandscape(
+                                    task.topActivityInfo.screenOrientation);
+                    taskInfo.isFixedOrientationPortrait =
+                            ActivityInfo.isFixedOrientationPortrait(
+                                    task.topActivityInfo.screenOrientation);
+                } else {
+                    Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
+                            + " doesn't match with camera client package name: " + packageName);
+                    return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+                }
+            } else {
+                Log.e(TAG, "Recent task list is empty!");
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+            }
+
             // TODO: Modify the sensor orientation in camera characteristics along with any 3A
             //  regions in capture requests/results to account for thea physical rotation. The
             //  former is somewhat tricky as it assumes that camera clients always check for the
             //  current value by retrieving the camera characteristics from the camera device.
-            TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
             if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
                         OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
                         UserHandle.getUserHandleForUid(taskInfo.userId)))) {
@@ -673,12 +657,6 @@
             CameraStatsJobService.schedule(mContext);
 
             try {
-                ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to register task stack listener!");
-            }
-
-            try {
                 int[] displayIds = WindowManagerGlobal.getWindowManagerService()
                         .registerDisplayWindowListener(mDisplayWindowListener);
                 for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/core/java/com/android/server/communal/OWNERS b/services/core/java/com/android/server/communal/OWNERS
index e499b160..b02883d 100644
--- a/services/core/java/com/android/server/communal/OWNERS
+++ b/services/core/java/com/android/server/communal/OWNERS
@@ -1,3 +1,4 @@
 brycelee@google.com
 justinkoh@google.com
-lusilva@google.com
\ No newline at end of file
+lusilva@google.com
+xilei@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index efd3037..0fd7cc1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -427,6 +427,11 @@
         onClientExit();
     }
 
+    @Override
+    public int getId() {
+        return mHostEndPointId;
+    }
+
     /**
      * Invoked when the underlying binder of this broker has died at the client process.
      */
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 99cb6f0..24008d0 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -167,6 +167,7 @@
     public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
             boolean userSet) {
         assertFlag();
+        final long callingId = Binder.clearCallingIdentity();
         try {
             if (grant) {
                 mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId);
@@ -180,13 +181,20 @@
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Could not reach system server", e);
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
         }
     }
 
     public void setNotificationPermission(PackagePermission pkgPerm) {
         assertFlag();
-        setNotificationPermission(
-                pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            setNotificationPermission(
+                    pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
     }
 
     public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 1e2eb5d..e99512d 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -293,6 +293,9 @@
             String primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
             if (primaryCpuAbi != null && !VMRuntime.is64BitAbi(primaryCpuAbi)) {
                 final String nativeLibPath = pkg.getNativeLibraryDir();
+                if (!(new File(nativeLibPath).exists())) {
+                    return;
+                }
                 try {
                     mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
                             nativeLibPath, userId);
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 3e849f8..dd66130 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -133,7 +133,7 @@
     }
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags,
+            @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits);
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -338,8 +338,8 @@
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
-    int[] getPackageGids(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
-            @UserIdInt int userId);
+    int[] getPackageGids(@NonNull String packageName,
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     int getTargetSdkVersion(@NonNull String packageName);
@@ -351,12 +351,12 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     ActivityInfo getReceiverInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     boolean canRequestPackageInstalls(@NonNull String packageName, int callingUid,
@@ -369,18 +369,18 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
-            @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId);
+            @PackageManager.PackageInfoFlagsBits long flags, int callingUid, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
-            @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId);
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
@@ -439,17 +439,18 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
     ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(@NonNull String[] permissions,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId);
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
-    List<ApplicationInfo> getInstalledApplications(@PackageManager.ApplicationInfoFlags long flags,
-            @UserIdInt int userId, int callingUid);
+    List<ApplicationInfo> getInstalledApplications(
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
+            int callingUid);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
     ProviderInfo resolveContentProvider(@NonNull String name,
-            @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid);
+            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId, int callingUid);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
@@ -463,7 +464,7 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
     ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName, int uid,
-            @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey);
+            @PackageManager.ComponentInfoFlagsBits long flags, @Nullable String metaDataKey);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @Nullable
@@ -560,7 +561,7 @@
     boolean canQueryPackage(int callingUid, @Nullable String targetPackageName);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
-    int getPackageUid(@NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+    int getPackageUid(@NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 59250c5..627970e 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -429,7 +429,7 @@
     }
 
     public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             int filterCallingUid, int userId, boolean resolveForStart,
             boolean allowDynamicSplits) {
@@ -541,14 +541,14 @@
     }
 
     public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return queryIntentActivitiesInternal(
                 intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
                 userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
     }
 
     public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int callingUid, boolean includeInstantApps) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         enforceCrossUserOrProfilePermission(callingUid,
@@ -625,7 +625,7 @@
     }
 
     protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int callingUid, String instantAppPkgName) {
         // reader
         String pkgName = intent.getPackage();
@@ -653,7 +653,7 @@
     }
 
     public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
             String pkgName, String instantAppPkgName) {
         // reader
@@ -786,7 +786,7 @@
     }
 
     public final ActivityInfo getActivityInfo(ComponentName component,
-            @PackageManager.ResolveInfoFlags long flags, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
     }
 
@@ -797,7 +797,7 @@
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
     public final ActivityInfo getActivityInfoInternal(ComponentName component,
-            @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
 
@@ -811,7 +811,7 @@
     }
 
     protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
-            @PackageManager.ResolveInfoFlags long flags, int filterCallingUid, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
         ParsedActivity a = mComponentResolver.getActivity(component);
 
         if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
@@ -879,7 +879,7 @@
     }
 
     public final ApplicationInfo getApplicationInfo(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
         return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
     }
 
@@ -890,7 +890,7 @@
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
     public final ApplicationInfo getApplicationInfoInternal(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
             int filterCallingUid, int userId) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForApplication(flags, userId);
@@ -905,7 +905,7 @@
     }
 
     protected ApplicationInfo getApplicationInfoInternalBody(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
             int filterCallingUid, int userId) {
         // writer
         // Normalize package name to handle renamed packages and static libs
@@ -1161,7 +1161,7 @@
     }
 
     public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
             int parentUserId) {
         if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
                 sourceUserId)) {
@@ -1426,7 +1426,7 @@
     }
 
     private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
         // first, check to see if we've got an instant app already installed
         final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
@@ -1532,7 +1532,7 @@
     }
 
     public final PackageInfo generatePackageInfo(PackageStateInternal ps,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         if (!mUserManager.exists(userId)) return null;
         if (ps == null) {
             return null;
@@ -1607,7 +1607,7 @@
     }
 
     public final PackageInfo getPackageInfo(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
                 flags, Binder.getCallingUid(), userId);
     }
@@ -1808,7 +1808,7 @@
     @Nullable
     private CrossProfileDomainInfo createForwardingResolveInfo(
             @NonNull CrossProfileIntentFilter filter, @NonNull Intent intent,
-            @Nullable String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            @Nullable String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int sourceUserId) {
         int targetUserId = filter.getTargetUserId();
         if (!isUserEnabled(targetUserId)) {
@@ -1970,7 +1970,7 @@
     }
 
     public final ServiceInfo getServiceInfo(ComponentName component,
-            @PackageManager.ResolveInfoFlags long flags, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
@@ -1981,7 +1981,7 @@
     }
 
     protected ServiceInfo getServiceInfoBody(ComponentName component,
-            @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
         ParsedService s = mComponentResolver.getService(component);
         if (DEBUG_PACKAGE_INFO) {
             Log.v(
@@ -2233,7 +2233,7 @@
     }
 
     private boolean filterStaticSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
-            int userId, @PackageManager.ComponentInfoFlags long flags) {
+            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
         // Callers can access only the static shared libs they depend on, otherwise they need to
         // explicitly ask for the static shared libraries given the caller is allowed to access
         // all static libs.
@@ -2289,7 +2289,7 @@
     }
 
     private boolean filterSdkLibPackage(@Nullable PackageStateInternal ps, int uid,
-            int userId, @PackageManager.ComponentInfoFlags long flags) {
+            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
         // Callers can access only the SDK libs they depend on, otherwise they need to
         // explicitly ask for the SDKs given the caller is allowed to access
         // all shared libs.
@@ -2346,7 +2346,7 @@
 
     @Override
     public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
-            int userId, @PackageManager.ComponentInfoFlags long flags) {
+            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
         return filterStaticSharedLibPackage(ps, uid, userId, flags) || filterSdkLibPackage(ps, uid,
                 userId, flags);
     }
@@ -2445,7 +2445,7 @@
      * activity was not set by the DPC.
      */
     public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
-            int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+            int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
         return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
                 intent, userId, resolvedType, flags);
     }
@@ -2486,7 +2486,7 @@
 
     private boolean isInstantAppResolutionAllowed(
             Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-            boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
+            boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
         if (mInstantAppResolverConnection == null) {
             return false;
         }
@@ -2526,7 +2526,7 @@
     // Or if there's already an ephemeral app installed that handles the action
     protected boolean isInstantAppResolutionAllowedBody(
             Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-            boolean skipPackageCheck, @PackageManager.ResolveInfoFlags long flags) {
+            boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
         final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
         for (int n = 0; n < count; n++) {
             final ResolveInfo info = resolvedActivities.get(n);
@@ -2558,7 +2558,7 @@
     }
 
     private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
         PersistentPreferredIntentResolver ppir =
                 mSettings.getPersistentPreferredActivities(userId);
         //TODO(b/158003772): Remove double query
@@ -2716,7 +2716,7 @@
     }
 
     public int getPackageUidInternal(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
         // reader
         final AndroidPackage p = mPackages.get(packageName);
         if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
@@ -3239,7 +3239,7 @@
 
     // The body of findPreferredActivity.
     protected PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityBody(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always,
             boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
             int callingUid, boolean isDeviceProvisioned) {
@@ -3448,7 +3448,7 @@
     }
 
     public final PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always,
             boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
 
@@ -3465,7 +3465,7 @@
     }
 
     public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean debug, int userId) {
         final int n = query.size();
         PersistentPreferredIntentResolver ppir =
@@ -3663,7 +3663,7 @@
 
     @Override
     public int[] getPackageGids(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId);
@@ -3740,7 +3740,7 @@
     @Nullable
     @Override
     public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
@@ -3773,7 +3773,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return null;
         Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
         final int callingUid = Binder.getCallingUid();
@@ -3902,7 +3902,8 @@
 
     @Override
     public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
-            @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+            @UserIdInt int userId) {
         List<VersionedPackage> versionedPackages = null;
         final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates();
         final int packageCount = packageStates.size();
@@ -3964,7 +3965,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
-            @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
                 "getDeclaredSharedLibraries");
@@ -4041,7 +4042,7 @@
     @Nullable
     @Override
     public ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId);
@@ -4515,7 +4516,7 @@
     @NonNull
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
-            @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForPackage(flags, userId);
@@ -4536,7 +4537,7 @@
     }
 
     private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageStateInternal ps,
-            String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlags long flags,
+            String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlagsBits long flags,
             int userId) {
         int numMatch = 0;
         for (int i=0; i<permissions.length; i++) {
@@ -4578,7 +4579,7 @@
     @NonNull
     @Override
     public List<ApplicationInfo> getInstalledApplications(
-            @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
             int callingUid) {
         if (getInstantAppPackageName(callingUid) != null) {
             return Collections.emptyList();
@@ -4655,7 +4656,8 @@
     @Nullable
     @Override
     public ProviderInfo resolveContentProvider(@NonNull String name,
-            @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+            int callingUid) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
         final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
@@ -4744,7 +4746,8 @@
     @NonNull
     @Override
     public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
-            int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+            int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+            @Nullable String metaDataKey) {
         final int callingUid = Binder.getCallingUid();
         final int userId = processName != null ? UserHandle.getUserId(uid)
                 : UserHandle.getCallingUserId();
@@ -5290,7 +5293,7 @@
 
     @Override
     public int getPackageUid(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         if (!mUserManager.exists(userId)) return -1;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId);
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index f180d19..bd730e9 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -314,7 +314,7 @@
 
     @Override
     public int[] getPackageGids(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getPackageGids(packageName, flags, userId);
         }
@@ -339,7 +339,7 @@
     @Nullable
     @Override
     public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getReceiverInfo(component, flags, userId);
         }
@@ -348,7 +348,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getSharedLibraries(packageName, flags, userId);
         }
@@ -365,7 +365,8 @@
 
     @Override
     public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
-            @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+            @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
         }
@@ -374,7 +375,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
-            @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getDeclaredSharedLibraries(packageName, flags, userId);
@@ -384,7 +385,7 @@
     @Nullable
     @Override
     public ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getProviderInfo(component, flags, userId);
         }
@@ -498,7 +499,7 @@
     @NonNull
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
-            @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getPackagesHoldingPermissions(permissions, flags, userId);
@@ -508,7 +509,7 @@
     @NonNull
     @Override
     public List<ApplicationInfo> getInstalledApplications(
-            @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
             int callingUid) {
         synchronized (mLock) {
             return super.getInstalledApplications(flags, userId, callingUid);
@@ -518,7 +519,8 @@
     @Nullable
     @Override
     public ProviderInfo resolveContentProvider(@NonNull String name,
-            @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+            int callingUid) {
         synchronized (mLock) {
             return super.resolveContentProvider(name, flags, userId, callingUid);
         }
@@ -544,7 +546,8 @@
     @NonNull
     @Override
     public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
-            int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+            int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+            @Nullable String metaDataKey) {
         synchronized (mLock) {
             return super.queryContentProviders(processName, uid, flags, metaDataKey);
         }
@@ -717,7 +720,7 @@
 
     @Override
     public int getPackageUid(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         synchronized (mLock) {
             return super.getPackageUid(packageName, flags, userId);
         }
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index a3cd092..e6ff836 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -97,7 +97,7 @@
     }
 
     public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             int filterCallingUid, int userId, boolean resolveForStart,
             boolean allowDynamicSplits) {
@@ -111,7 +111,7 @@
         }
     }
     public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         ThreadComputer current = snapshot();
         try {
             return current.mComputer.queryIntentActivitiesInternal(intent, resolvedType, flags,
@@ -121,7 +121,7 @@
         }
     }
     public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int callingUid, boolean includeInstantApps) {
         ThreadComputer current = snapshot();
         try {
@@ -132,7 +132,7 @@
         }
     }
     public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
             String pkgName, String instantAppPkgName) {
         ThreadComputer current = live();
@@ -145,7 +145,7 @@
         }
     }
     public ActivityInfo getActivityInfo(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
         ThreadComputer current = snapshot();
         try {
             return current.mComputer.getActivityInfo(component, flags, userId);
@@ -154,7 +154,7 @@
         }
     }
     public ActivityInfo getActivityInfoInternal(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags,
+            @PackageManager.ComponentInfoFlagsBits long flags,
             int filterCallingUid, int userId) {
         ThreadComputer current = live();
         try {
@@ -191,7 +191,7 @@
         }
     }
     public ApplicationInfo getApplicationInfo(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
         ThreadComputer current = snapshot();
         try {
             return current.mComputer.getApplicationInfo(packageName, flags, userId);
@@ -200,7 +200,7 @@
         }
     }
     public ApplicationInfo getApplicationInfoInternal(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
         ThreadComputer current = live();
         try {
             return current.mComputer.getApplicationInfoInternal(packageName, flags,
@@ -227,7 +227,7 @@
         }
     }
     public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
             int parentUserId) {
         ThreadComputer current = live();
         try {
@@ -268,7 +268,7 @@
         }
     }
     public PackageInfo generatePackageInfo(PackageStateInternal ps,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         ThreadComputer current = live();
         try {
             return current.mComputer.generatePackageInfo(ps, flags, userId);
@@ -277,7 +277,7 @@
         }
     }
     public PackageInfo getPackageInfo(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         ThreadComputer current = snapshot();
         try {
             return current.mComputer.getPackageInfo(packageName, flags, userId);
@@ -341,7 +341,7 @@
         }
     }
     public ServiceInfo getServiceInfo(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
         ThreadComputer current = live();
         try {
             return current.mComputer.getServiceInfo(component, flags, userId);
@@ -446,7 +446,7 @@
         }
     }
     public boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
-            int userId, @PackageManager.ComponentInfoFlags long flags) {
+            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
         ThreadComputer current = live();
         try {
             return current.mComputer.filterSharedLibPackage(ps, uid, userId, flags);
@@ -480,7 +480,7 @@
         }
     }
     public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
-            int userId, String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+            int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
         ThreadComputer current = live();
         try {
             return current.mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent,
@@ -553,7 +553,7 @@
         }
     }
     public int getPackageUidInternal(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
         ThreadComputer current = live();
         try {
             return current.mComputer.getPackageUidInternal(packageName, flags, userId,
@@ -648,7 +648,7 @@
         }
     }
     public PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
             int userId, boolean queryMayBeFiltered) {
         ThreadComputer current = live();
@@ -660,7 +660,7 @@
         }
     }
     public ResolveInfo findPersistentPreferredActivityLP(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean debug, int userId) {
         ThreadComputer current = live();
         try {
@@ -749,7 +749,7 @@
 
     @Override
     public int[] getPackageGids(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getPackageGids(packageName, flags, userId);
         }
@@ -774,7 +774,7 @@
     @Nullable
     @Override
     public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getReceiverInfo(component, flags, userId);
         }
@@ -783,7 +783,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getSharedLibraries(packageName, flags, userId);
         }
@@ -808,7 +808,8 @@
 
     @Override
     public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo,
-            @PackageManager.PackageInfoFlags long flags, int callingUid, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int callingUid,
+            @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid,
                     userId);
@@ -818,7 +819,7 @@
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
-            @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
@@ -828,7 +829,7 @@
     @Nullable
     @Override
     public ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getProviderInfo(component, flags, userId);
         }
@@ -943,7 +944,7 @@
     @NonNull
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
-            @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
@@ -953,7 +954,7 @@
     @NonNull
     @Override
     public List<ApplicationInfo> getInstalledApplications(
-            @PackageManager.ApplicationInfoFlags long flags, @UserIdInt int userId,
+            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
             int callingUid) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getInstalledApplications(flags, userId, callingUid);
@@ -963,7 +964,8 @@
     @Nullable
     @Override
     public ProviderInfo resolveContentProvider(@NonNull String name,
-            @PackageManager.ResolveInfoFlags long flags, @UserIdInt int userId, int callingUid) {
+            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
+            int callingUid) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.resolveContentProvider(name, flags, userId, callingUid);
         }
@@ -990,7 +992,8 @@
     @NonNull
     @Override
     public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
-            int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+            int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+            @Nullable String metaDataKey) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
         }
@@ -1164,7 +1167,7 @@
 
     @Override
     public int getPackageUid(@NonNull String packageName,
-            @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         try (ThreadComputer current = snapshot()) {
             return current.mComputer.getPackageUid(packageName, flags, userId);
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9c11cd4..6e6773f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -121,6 +121,7 @@
     public void onStart() {
         publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
         mLauncherAppsImpl.registerLoadingProgressForIncrementalApps();
+        LocalServices.addService(LauncherAppsServiceInternal.class, mLauncherAppsImpl.mInternal);
     }
 
     static class BroadcastCookie {
@@ -137,6 +138,17 @@
         }
     }
 
+    /**
+     * Local system service interface.
+     * @hide Only for use within system server
+     */
+    public abstract static class LauncherAppsServiceInternal {
+        /** Same as startShortcut except supports forwarding of caller uid/pid. */
+        public abstract boolean startShortcut(int callerUid, int callerPid, String callingPackage,
+                String packageName, String featureId, String shortcutId, Rect sourceBounds,
+                Bundle startActivityOptions, int targetUserId);
+    }
+
     @VisibleForTesting
     static class LauncherAppsImpl extends ILauncherApps.Stub {
         private static final boolean DEBUG = false;
@@ -168,6 +180,8 @@
 
         private PackageInstallerService mPackageInstallerService;
 
+        final LauncherAppsServiceInternal mInternal;
+
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mIPM = AppGlobals.getPackageManager();
@@ -189,6 +203,7 @@
             mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
             mCallbackHandler = BackgroundThread.getHandler();
             mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            mInternal = new LocalService();
         }
 
         @VisibleForTesting
@@ -359,11 +374,15 @@
          * group.
          */
         private boolean canAccessProfile(int targetUserId, String message) {
-            final int callingUserId = injectCallingUserId();
+            return canAccessProfile(injectBinderCallingUid(), injectCallingUserId(),
+                    injectBinderCallingPid(), targetUserId, message);
+        }
+
+        private boolean canAccessProfile(int callingUid, int callingUserId, int callingPid,
+                int targetUserId, String message) {
 
             if (targetUserId == callingUserId) return true;
-            if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
-                    injectBinderCallingUid())) {
+            if (injectHasInteractAcrossUsersFullPermission(callingPid, callingUid)) {
                 return true;
             }
 
@@ -379,25 +398,29 @@
                 injectRestoreCallingIdentity(ident);
             }
 
-            return mUserManagerInternal.isProfileAccessible(injectCallingUserId(), targetUserId,
+            return mUserManagerInternal.isProfileAccessible(callingUserId, targetUserId,
                     message, true);
         }
 
+        private void verifyCallingPackage(String callingPackage) {
+            verifyCallingPackage(callingPackage, injectBinderCallingUid());
+        }
+
         @VisibleForTesting // We override it in unit tests
-        void verifyCallingPackage(String callingPackage) {
+        void verifyCallingPackage(String callingPackage, int callerUid) {
             int packageUid = -1;
             try {
                 packageUid = mIPM.getPackageUid(callingPackage,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                                 | PackageManager.MATCH_UNINSTALLED_PACKAGES,
-                        UserHandle.getUserId(getCallingUid()));
+                        UserHandle.getUserId(callerUid));
             } catch (RemoteException ignore) {
             }
             if (packageUid < 0) {
                 Log.e(TAG, "Package not found: " + callingPackage);
             }
-            if (packageUid != injectBinderCallingUid()) {
+            if (packageUid != callerUid) {
                 throw new SecurityException("Calling package name mismatch");
             }
         }
@@ -797,9 +820,15 @@
         }
 
         private void ensureShortcutPermission(@NonNull String callingPackage) {
-            verifyCallingPackage(callingPackage);
-            if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
-                    callingPackage, injectBinderCallingPid(), injectBinderCallingUid())) {
+            ensureShortcutPermission(injectBinderCallingUid(), injectBinderCallingPid(),
+                    callingPackage);
+        }
+
+        private void ensureShortcutPermission(int callerUid, int callerPid,
+                @NonNull String callingPackage) {
+            verifyCallingPackage(callingPackage, callerUid);
+            if (!mShortcutServiceInternal.hasShortcutHostPermission(UserHandle.getUserId(callerUid),
+                    callingPackage, callerPid, callerUid)) {
                 throw new SecurityException("Caller can't access shortcut information");
             }
         }
@@ -989,20 +1018,28 @@
         public boolean startShortcut(String callingPackage, String packageName, String featureId,
                 String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
                 int targetUserId) {
-            verifyCallingPackage(callingPackage);
+            return startShortcutInner(injectBinderCallingUid(), injectBinderCallingPid(),
+                    injectCallingUserId(), callingPackage, packageName, featureId, shortcutId,
+                    sourceBounds, startActivityOptions, targetUserId);
+        }
+
+        private boolean startShortcutInner(int callerUid, int callerPid, int callingUserId,
+                String callingPackage, String packageName, String featureId, String shortcutId,
+                Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
+            verifyCallingPackage(callingPackage, callerUid);
             if (!canAccessProfile(targetUserId, "Cannot start activity")) {
                 return false;
             }
 
             // Even without the permission, pinned shortcuts are always launchable.
-            if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
+            if (!mShortcutServiceInternal.isPinnedByCaller(callingUserId,
                     callingPackage, packageName, shortcutId, targetUserId)) {
-                ensureShortcutPermission(callingPackage);
+                ensureShortcutPermission(callerUid, callerPid, callingPackage);
             }
 
             final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
-                    getCallingUserId(), callingPackage, packageName, shortcutId, targetUserId,
-                    injectBinderCallingPid(), injectBinderCallingUid());
+                    callingUserId, callingPackage, packageName, shortcutId, targetUserId,
+                    callerPid, callerUid);
             if (intents == null || intents.length == 0) {
                 return false;
             }
@@ -1023,7 +1060,7 @@
 
             // Replace theme for splash screen
             final String splashScreenThemeResName =
-                    mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
+                    mShortcutServiceInternal.getShortcutStartingThemeResName(callingUserId,
                             callingPackage, packageName, shortcutId, targetUserId);
             if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
                 if (startActivityOptions == null) {
@@ -1809,5 +1846,16 @@
                 }
             }
         }
+
+        final class LocalService extends LauncherAppsServiceInternal {
+            @Override
+            public boolean startShortcut(int callerUid, int callerPid, String callingPackage,
+                    String packageName, String featureId, String shortcutId, Rect sourceBounds,
+                    Bundle startActivityOptions, int targetUserId) {
+                return LauncherAppsImpl.this.startShortcutInner(callerUid, callerPid,
+                        UserHandle.getUserId(callerUid), callingPackage, packageName, featureId,
+                        shortcutId, sourceBounds, startActivityOptions, targetUserId);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fa23000..7ceae61 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2582,7 +2582,7 @@
     }
 
     private PackageInfo generatePackageInfo(@NonNull PackageStateInternal ps,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return mComputer.generatePackageInfo(ps, flags, userId);
     }
 
@@ -2622,13 +2622,13 @@
 
     @Override
     public PackageInfo getPackageInfo(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return mComputer.getPackageInfo(packageName, flags, userId);
     }
 
     @Override
     public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return mComputer.getPackageInfoInternal(versionedPackage.getPackageName(),
                 versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
     }
@@ -2665,7 +2665,7 @@
     }
 
     private boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
-            int userId, @PackageManager.ComponentInfoFlags long flags) {
+            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
         return mComputer.filterSharedLibPackage(ps, uid, userId, flags);
     }
 
@@ -2681,17 +2681,17 @@
 
     @Override
     public int getPackageUid(@NonNull String packageName,
-        @PackageManager.PackageInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
         return mComputer.getPackageUid(packageName, flags, userId);
     }
 
     private int getPackageUidInternal(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId, int callingUid) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
         return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
     }
 
     @Override
-    public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlags long flags,
+    public int[] getPackageGids(String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             int userId) {
         return mComputer.getPackageGids(packageName, flags, userId);
     }
@@ -2706,14 +2706,14 @@
     }
 
     private ApplicationInfo generateApplicationInfoFromSettings(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags, int filterCallingUid, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
         return mComputer.generateApplicationInfoFromSettings(packageName, flags, filterCallingUid,
                 userId);
     }
 
     @Override
     public ApplicationInfo getApplicationInfo(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
         return mComputer.getApplicationInfo(packageName, flags, userId);
     }
 
@@ -2724,7 +2724,7 @@
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
     private ApplicationInfo getApplicationInfoInternal(String packageName,
-            @PackageManager.ApplicationInfoFlags long flags,
+            @PackageManager.ApplicationInfoFlagsBits long flags,
             int filterCallingUid, int userId) {
         return mComputer.getApplicationInfoInternal(packageName, flags,
                 filterCallingUid, userId);
@@ -3011,7 +3011,7 @@
 
     @Override
     public ActivityInfo getActivityInfo(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
         return mComputer.getActivityInfo(component, flags, userId);
     }
 
@@ -3022,7 +3022,7 @@
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
     private ActivityInfo getActivityInfoInternal(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, int filterCallingUid, int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, int filterCallingUid, int userId) {
         return mComputer.getActivityInfoInternal(component, flags,
                 filterCallingUid, userId);
     }
@@ -3036,42 +3036,42 @@
 
     @Override
     public ActivityInfo getReceiverInfo(ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
         return mComputer.getReceiverInfo(component, flags, userId);
     }
 
     @Override
     public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(String packageName,
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return mComputer.getSharedLibraries(packageName, flags, userId);
     }
 
     @Nullable
     @Override
     public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
-            @NonNull String packageName, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
             @NonNull int userId) {
         return mComputer.getDeclaredSharedLibraries(packageName, flags, userId);
     }
 
     @Nullable
     List<VersionedPackage> getPackagesUsingSharedLibrary(
-            SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlags long flags, int callingUid,
-            int userId) {
+            SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
+            int callingUid, int userId) {
         return mComputer.getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId);
     }
 
     @Nullable
     @Override
     public ServiceInfo getServiceInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         return mComputer.getServiceInfo(component, flags, userId);
     }
 
     @Nullable
     @Override
     public ProviderInfo getProviderInfo(@NonNull ComponentName component,
-            @PackageManager.ComponentInfoFlags long flags, @UserIdInt int userId) {
+            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
         return mComputer.getProviderInfo(component, flags, userId);
     }
 
@@ -3360,7 +3360,7 @@
 
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
                 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
     }
@@ -3405,7 +3405,7 @@
      */
     @GuardedBy("mLock")
     boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
         return mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
                 resolvedType, flags);
     }
@@ -3413,7 +3413,7 @@
     @GuardedBy("mLock")
     ResolveInfo findPersistentPreferredActivityLP(Intent intent,
             String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean debug,
+            @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query, boolean debug,
             int userId) {
         return mComputer.findPersistentPreferredActivityLP(intent,
                 resolvedType, flags, query, debug, userId);
@@ -3427,7 +3427,7 @@
     }
 
     FindPreferredActivityBodyResult findPreferredActivityInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always,
             boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
         return mComputer.findPreferredActivityInternal(
@@ -3457,7 +3457,7 @@
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
 
@@ -3477,13 +3477,13 @@
     }
 
     @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return mComputer.queryIntentActivitiesInternal(intent,
                 resolvedType, flags, userId);
     }
 
     @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             @PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
             boolean resolveForStart, boolean allowDynamicSplits) {
         return mComputer.queryIntentActivitiesInternal(intent,
@@ -3492,7 +3492,7 @@
     }
 
     private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int sourceUserId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
             int parentUserId) {
         return mComputer.getCrossProfileDomainPreferredLpr(intent,
                 resolvedType, flags, sourceUserId, parentUserId);
@@ -3520,21 +3520,21 @@
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
             Intent[] specifics, String[] specificTypes, Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
                 caller, specifics, specificTypes, intent, resolvedType, flags, userId));
     }
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
                 resolvedType, flags, userId, Binder.getCallingUid()));
     }
 
     @Override
     public ResolveInfo resolveService(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         final int callingUid = Binder.getCallingUid();
         return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
                 callingUid);
@@ -3542,14 +3542,14 @@
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         final int callingUid = Binder.getCallingUid();
         return new ParceledListSlice<>(queryIntentServicesInternal(
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
     }
 
     @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int callingUid, boolean includeInstantApps) {
         return mComputer.queryIntentServicesInternal(intent,
                 resolvedType, flags, userId, callingUid,
@@ -3558,27 +3558,27 @@
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
                 intent, resolvedType, flags, userId));
     }
 
     @Override
     public ParceledListSlice<PackageInfo> getInstalledPackages(
-            @PackageManager.PackageInfoFlags long flags, int userId) {
+            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
         return mComputer.getInstalledPackages(flags, userId);
     }
 
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
-            @NonNull String[] permissions, @PackageManager.PackageInfoFlags long flags,
+            @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
             @UserIdInt int userId) {
         return mComputer.getPackagesHoldingPermissions(permissions, flags, userId);
     }
 
     @Override
     public ParceledListSlice<ApplicationInfo> getInstalledApplications(
-            @PackageManager.ApplicationInfoFlags long flags, int userId) {
+            @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
         final int callingUid = Binder.getCallingUid();
         return new ParceledListSlice<>(
                 mComputer.getInstalledApplications(flags, userId, callingUid));
@@ -3683,7 +3683,7 @@
 
     @Override
     public ProviderInfo resolveContentProvider(String name,
-            @PackageManager.ResolveInfoFlags long flags, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return mComputer.resolveContentProvider(name, flags, userId, Binder.getCallingUid());
     }
 
@@ -3695,7 +3695,8 @@
     @NonNull
     @Override
     public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable  String processName,
-            int uid, @PackageManager.ComponentInfoFlags long flags, @Nullable String metaDataKey) {
+            int uid, @PackageManager.ComponentInfoFlagsBits long flags,
+            @Nullable String metaDataKey) {
         return mComputer.queryContentProviders(processName, uid, flags, metaDataKey);
     }
 
@@ -7575,7 +7576,7 @@
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public List<ApplicationInfo> getInstalledApplications(
-                @PackageManager.ApplicationInfoFlags long flags, int userId, int callingUid) {
+                @PackageManager.ApplicationInfoFlagsBits long flags, int userId, int callingUid) {
             return PackageManagerService.this.mComputer.getInstalledApplications(flags, userId,
                     callingUid);
         }
@@ -7770,7 +7771,7 @@
 
         @Override
         public PackageInfo getPackageInfo(
-                String packageName, @PackageManager.PackageInfoFlags long flags,
+                String packageName, @PackageManager.PackageInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this.mComputer
                     .getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
@@ -7899,15 +7900,15 @@
         }
 
         @Override
-        public int getPackageUid(String packageName, @PackageManager.PackageInfoFlags long flags,
-                int userId) {
+        public int getPackageUid(String packageName,
+                @PackageManager.PackageInfoFlagsBits long flags, int userId) {
             return PackageManagerService.this
                     .getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
         }
 
         @Override
         public ApplicationInfo getApplicationInfo(
-                String packageName, @PackageManager.ApplicationInfoFlags long flags,
+                String packageName, @PackageManager.ApplicationInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this
                     .getApplicationInfoInternal(packageName, flags, filterCallingUid, userId);
@@ -7915,7 +7916,7 @@
 
         @Override
         public ActivityInfo getActivityInfo(
-                ComponentName component, @PackageManager.ComponentInfoFlags long flags,
+                ComponentName component, @PackageManager.ComponentInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this
                     .getActivityInfoInternal(component, flags, filterCallingUid, userId);
@@ -7923,7 +7924,7 @@
 
         @Override
         public List<ResolveInfo> queryIntentActivities(
-                Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+                Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this
                     .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
@@ -7932,7 +7933,7 @@
 
         @Override
         public List<ResolveInfo> queryIntentReceivers(Intent intent,
-                String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+                String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
                     intent, resolvedType, flags, userId, filterCallingUid);
@@ -7940,7 +7941,7 @@
 
         @Override
         public List<ResolveInfo> queryIntentServices(
-                Intent intent, @PackageManager.ResolveInfoFlags long flags, int callingUid,
+                Intent intent, @PackageManager.ResolveInfoFlagsBits long flags, int callingUid,
                 int userId) {
             final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
             return PackageManagerService.this
@@ -8209,7 +8210,7 @@
 
         @Override
         public ResolveInfo resolveIntent(Intent intent, String resolvedType,
-                @PackageManager.ResolveInfoFlags long flags,
+                @PackageManager.ResolveInfoFlagsBits long flags,
                 @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
                 boolean resolveForStart, int filterCallingUid) {
             return mResolveIntentHelper.resolveIntentInternal(
@@ -8219,14 +8220,14 @@
 
         @Override
         public ResolveInfo resolveService(Intent intent, String resolvedType,
-                @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+                @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
             return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
                     callingUid);
         }
 
         @Override
         public ProviderInfo resolveContentProvider(String name,
-                @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+                @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
             return PackageManagerService.this.mComputer
                     .resolveContentProvider(name, flags, userId,callingUid);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 3b643b5..4082afd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1079,7 +1079,7 @@
     public static boolean hasAnyDomainApproval(
             @NonNull DomainVerificationManagerInternal manager,
             @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
-            @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId) {
+            @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId) {
         return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
                 > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
     }
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 8c91b16..802f701 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -75,8 +75,8 @@
     }
 
     private ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags, List<ResolveInfo> query, boolean always,
-            boolean removeMatches, boolean debug, int userId) {
+            @PackageManager.ResolveInfoFlagsBits long flags, List<ResolveInfo> query,
+            boolean always, boolean removeMatches, boolean debug, int userId) {
         return findPreferredActivityNotLocked(
                 intent, resolvedType, flags, query, always, removeMatches, debug, userId,
                 UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
@@ -85,7 +85,7 @@
     // TODO: handle preferred activities missing while user has amnesia
     /** <b>must not hold {@link PackageManagerService.mLock}</b> */
     public ResolveInfo findPreferredActivityNotLocked(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
             int userId, boolean queryMayBeFiltered) {
         if (Thread.holdsLock(mPm.mLock)) {
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 0ee1f89..2aff90f 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -75,7 +75,7 @@
      * since we need to allow the system to start any installed application.
      */
     public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags,
+            @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
             boolean resolveForStart, int filterCallingUid) {
         try {
@@ -115,7 +115,7 @@
     }
 
     private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags,
+            @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
         if (query != null) {
@@ -278,7 +278,7 @@
     // In this method, we have to know the actual calling UID, but in some cases Binder's
     // call identity is removed, so the UID has to be passed in explicitly.
     public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int filterCallingUid) {
         if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
         mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
@@ -374,7 +374,7 @@
 
 
     public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlags long flags, int userId, int callingUid) {
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
         if (!mPm.mUserManager.exists(userId)) return null;
         flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
@@ -391,7 +391,7 @@
     }
 
     public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlags long flags,
+            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int userId) {
         if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
@@ -533,7 +533,7 @@
 
     public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
             Intent[] specifics, String[] specificTypes, Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlags long flags, int userId) {
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
         flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bc4c8b0..167ad3b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -839,7 +839,7 @@
     @Override
     public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
             boolean excludePreCreated) {
-        checkManageOrCreateUsersPermission("query users");
+        checkCreateUsersPermission("query users");
         return getUsersInternal(excludePartial, excludeDying, excludePreCreated);
     }
 
@@ -865,10 +865,10 @@
     public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) {
         boolean returnFullInfo;
         if (userId != UserHandle.getCallingUserId()) {
-            checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
+            checkQueryOrCreateUsersPermission("getting profiles related to user " + userId);
             returnFullInfo = true;
         } else {
-            returnFullInfo = hasManageOrCreateUsersPermission();
+            returnFullInfo = hasCreateUsersPermission();
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -898,7 +898,7 @@
     public int[] getProfileIds(@UserIdInt int userId, @Nullable String userType,
             boolean enabledOnly) {
         if (userId != UserHandle.getCallingUserId()) {
-            checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
+            checkQueryOrCreateUsersPermission("getting profiles related to user " + userId);
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -987,7 +987,7 @@
     @Override
     public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
         if (userId == otherUserId) return true;
-        checkManageUsersPermission("check if in the same profile group");
+        checkQueryUsersPermission("check if in the same profile group");
         return isSameProfileGroupNoChecks(userId, otherUserId);
     }
 
@@ -1388,7 +1388,7 @@
 
     @Override
     public UserInfo getUserInfo(@UserIdInt int userId) {
-        checkManageOrCreateUsersPermission("query user");
+        checkQueryOrCreateUsersPermission("query user");
         synchronized (mUsersLock) {
             return userWithName(getUserInfoLU(userId));
         }
@@ -1519,7 +1519,7 @@
 
     @Override
     public boolean isProfile(@UserIdInt int userId) {
-        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
+        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isProfile();
@@ -1528,7 +1528,7 @@
 
     @Override
     public boolean isManagedProfile(@UserIdInt int userId) {
-        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+        checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isManagedProfile();
@@ -1592,7 +1592,7 @@
     @Override
     public String getUserName() {
         final int callingUid = Binder.getCallingUid();
-        if (!hasManageOrCreateUsersPermission()
+        if (!hasQueryOrCreateUsersPermission()
                 && !hasPermissionGranted(
                         android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
             throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
@@ -1628,18 +1628,59 @@
         }
     }
 
+    /**
+     * Enforces that the calling user is in the same profile group as {@code userId} or that only
+     * the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS INTERACT_ACROSS_USERS}
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
+     * can make certain calls to the UserManager.
+     *
+     * @param name used as message if SecurityException is thrown
+     * @throws SecurityException if the caller lacks the required permissions.
+     */
     private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdInt int userId,
             String name) {
         final int callingUserId = UserHandle.getCallingUserId();
-        if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
-                hasManageUsersPermission()) {
+        if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId)) {
             return;
         }
-        if (!hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS,
-                Binder.getCallingUid())) {
-            throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission "
-                    + "to: check " + name);
+        if (hasManageUsersPermission()) {
+            return;
         }
+        if (hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS,
+                Binder.getCallingUid())) {
+            return;
+        }
+        throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS permission "
+                + "to: check " + name);
+    }
+
+    /**
+     * Enforces that the calling user is in the same profile group as {@code userId} or that only
+     * the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS INTERACT_ACROSS_USERS}
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+     * can make certain calls to the UserManager.
+     *
+     * @param name used as message if SecurityException is thrown
+     * @throws SecurityException if the caller lacks the required permissions.
+     */
+    private void checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(
+            @UserIdInt int userId, String name) {
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId)) {
+            return;
+        }
+        if (hasQueryUsersPermission()) {
+            return;
+        }
+        if (hasPermissionGranted(
+                Manifest.permission.INTERACT_ACROSS_USERS, Binder.getCallingUid())) {
+            return;
+        }
+        throw new SecurityException("You need INTERACT_ACROSS_USERS, MANAGE_USERS, or QUERY_USERS "
+                + "permission to: check " + name);
     }
 
     @Override
@@ -1667,7 +1708,7 @@
     @Override
     public boolean isRestricted(@UserIdInt int userId) {
         if (userId != UserHandle.getCallingUserId()) {
-            checkManageOrCreateUsersPermission("query isRestricted for user " + userId);
+            checkCreateUsersPermission("query isRestricted for user " + userId);
         }
         synchronized (mUsersLock) {
             final UserInfo userInfo = getUserInfoLU(userId);
@@ -2147,7 +2188,7 @@
     @Override
     public List<EnforcingUser> getUserRestrictionSources(
             String restrictionKey, @UserIdInt int userId) {
-        checkManageUsersPermission("getUserRestrictionSource");
+        checkQueryUsersPermission("call getUserRestrictionSources.");
 
         // Shortcut for the most common case
         if (!hasUserRestriction(restrictionKey, userId)) {
@@ -2186,7 +2227,7 @@
 
     @Override
     public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) {
-        checkManageOrCreateUsersPermission("hasBaseUserRestriction");
+        checkCreateUsersPermission("hasBaseUserRestriction");
         if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
             return false;
         }
@@ -2403,7 +2444,7 @@
      */
     @Override
     public boolean canAddMoreUsersOfType(String userType) {
-        checkManageOrCreateUsersPermission("check if more users can be added.");
+        checkCreateUsersPermission("check if more users can be added.");
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         return userTypeDetails != null && canAddMoreUsersOfType(userTypeDetails);
     }
@@ -2411,7 +2452,7 @@
     /** Returns whether the creation of users of the given user type is enabled on this device. */
     @Override
     public boolean isUserTypeEnabled(String userType) {
-        checkManageOrCreateUsersPermission("check if user type is enabled.");
+        checkCreateUsersPermission("check if user type is enabled.");
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         return userTypeDetails != null && userTypeDetails.isEnabled();
     }
@@ -2426,7 +2467,7 @@
     @Override
     public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
             boolean allowedToRemoveOne) {
-        checkManageUsersPermission("check if more profiles can be added.");
+        checkQueryUsersPermission("check if more profiles can be added.");
         final UserTypeDetails type = mUserTypes.get(userType);
         if (type == null || !type.isEnabled()) {
             return false;
@@ -2536,24 +2577,58 @@
      *
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
-     * @see #hasManageOrCreateUsersPermission()
+     * @see #hasCreateUsersPermission()
      */
-    private static final void checkManageOrCreateUsersPermission(String message) {
-        if (!hasManageOrCreateUsersPermission()) {
+    private static final void checkCreateUsersPermission(String message) {
+        if (!hasCreateUsersPermission()) {
             throw new SecurityException(
                     "You either need MANAGE_USERS or CREATE_USERS permission to: " + message);
         }
     }
 
     /**
-     * Similar to {@link #checkManageOrCreateUsersPermission(String)} but when the caller is tries
+    * Enforces that only the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+     * can make certain calls to the UserManager.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller lacks the required permissions.
+     */
+    private static final void checkQueryUsersPermission(String message) {
+        if (!hasQueryUsersPermission()) {
+            throw new SecurityException(
+                    "You either need MANAGE_USERS or QUERY_USERS permission to: " + message);
+        }
+    }
+
+    /**
+     * Enforces that only the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+     * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}
+     * can make certain calls to the UserManager.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller lacks the required permissions.
+     */
+    private static final void checkQueryOrCreateUsersPermission(String message) {
+        if (!hasQueryOrCreateUsersPermission()) {
+            throw new SecurityException(
+                    "You either need MANAGE_USERS, CREATE_USERS, or QUERY_USERS permission to: "
+                            + message);
+        }
+    }
+
+    /**
+     * Similar to {@link #checkCreateUsersPermission(String)} but when the caller is tries
      * to create user/profiles other than what is allowed for
      * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} permission, then it will only
      * allow callers with {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} permission.
      */
-    private static final void checkManageOrCreateUsersPermission(int creationFlags) {
+    private static final void checkCreateUsersPermission(int creationFlags) {
         if ((creationFlags & ~ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION) == 0) {
-            if (!hasManageOrCreateUsersPermission()) {
+            if (!hasCreateUsersPermission()) {
                 throw new SecurityException("You either need MANAGE_USERS or CREATE_USERS "
                         + "permission to create an user with flags: " + creationFlags);
             }
@@ -2597,11 +2672,31 @@
      * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
      * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}.
      */
-    private static final boolean hasManageOrCreateUsersPermission() {
+    private static final boolean hasCreateUsersPermission() {
         return hasManageUsersOrPermission(android.Manifest.permission.CREATE_USERS);
     }
 
     /**
+     * @return whether the calling UID is system UID or root's UID or the calling app has the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}.
+     */
+    private static final boolean hasQueryUsersPermission() {
+        return hasManageUsersOrPermission(android.Manifest.permission.QUERY_USERS);
+    }
+
+    /**
+     * @return whether the calling UID is system UID or root's UID or the calling app has
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+     * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} or
+     * {@link android.Manifest.permission#QUERY_USERS QUERY_USERS}.
+     */
+    private static final boolean hasQueryOrCreateUsersPermission() {
+        return hasCreateUsersPermission()
+                || hasPermissionGranted(Manifest.permission.QUERY_USERS, Binder.getCallingUid());
+    }
+
+    /**
      * Enforces that only the system UID or root's UID (on any user) can make certain calls to the
      * UserManager.
      *
@@ -3477,7 +3572,7 @@
     public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
-        checkManageOrCreateUsersPermission(flags);
+        checkCreateUsersPermission(flags);
         try {
             return createUserInternal(name, userType, flags, userId, disallowedPackages);
         } catch (UserManager.CheckedUserOperationException e) {
@@ -3493,7 +3588,7 @@
             @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
-        checkManageOrCreateUsersPermission(flags);
+        checkCreateUsersPermission(flags);
         try {
             return createUserInternalUnchecked(name, userType, flags, userId,
                     /* preCreate= */ false, disallowedPackages, /* token= */ null);
@@ -3506,7 +3601,7 @@
     public UserInfo createUserWithThrow(String name, @NonNull String userType,
             @UserInfoFlag int flags)
             throws ServiceSpecificException {
-        checkManageOrCreateUsersPermission(flags);
+        checkCreateUsersPermission(flags);
         try {
             return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
                     /* disallowedPackages= */ null);
@@ -3520,7 +3615,7 @@
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
 
-        checkManageOrCreateUsersPermission(flags);
+        checkCreateUsersPermission(flags);
 
         Preconditions.checkArgument(isUserTypeEligibleForPreCreation(userTypeDetails),
                 "cannot pre-create user of type " + userType);
@@ -3540,7 +3635,7 @@
             String userName, String userType, @UserInfoFlag int flags,
             Bitmap userIcon,
             String accountName, String accountType, PersistableBundle accountOptions) {
-        checkManageOrCreateUsersPermission(flags);
+        checkCreateUsersPermission(flags);
 
         if (someUserHasAccountNoChecks(accountName, accountType)) {
             throw new ServiceSpecificException(
@@ -3985,7 +4080,7 @@
 
     @Override
     public String[] getPreInstallableSystemPackages(@NonNull String userType) {
-        checkManageOrCreateUsersPermission("getPreInstallableSystemPackages");
+        checkCreateUsersPermission("getPreInstallableSystemPackages");
         final Set<String> installableSystemPackages =
                 mSystemPackageInstaller.getInstallablePackagesForUserType(userType);
         if (installableSystemPackages == null) {
@@ -4110,7 +4205,7 @@
      */
     @Override
     public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) {
-        checkManageOrCreateUsersPermission("setupRestrictedProfile");
+        checkCreateUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUserWithThrow(
                 name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
         if (user == null) {
@@ -4207,7 +4302,7 @@
     @Override
     public boolean removeUser(@UserIdInt int userId) {
         Slog.i(LOG_TAG, "removeUser u" + userId);
-        checkManageOrCreateUsersPermission("Only the system can remove users");
+        checkCreateUsersPermission("Only the system can remove users");
 
         final String restriction = getUserRemovalRestriction(userId);
         if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
@@ -4219,7 +4314,7 @@
 
     @Override
     public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
-        checkManageOrCreateUsersPermission("Only the system can remove users");
+        checkCreateUsersPermission("Only the system can remove users");
         return removeUserUnchecked(userId);
     }
 
@@ -4334,7 +4429,7 @@
     @Override
     public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
             boolean evenWhenDisallowed) {
-        checkManageOrCreateUsersPermission("Only the system can remove users");
+        checkCreateUsersPermission("Only the system can remove users");
 
         if (!evenWhenDisallowed) {
             final String restriction = getUserRemovalRestriction(userId);
@@ -5085,7 +5180,7 @@
 
     @Override
     public boolean someUserHasAccount(String accountName, String accountType) {
-        checkManageOrCreateUsersPermission("check seed account information");
+        checkCreateUsersPermission("check seed account information");
         return someUserHasAccountNoChecks(accountName, accountType);
     }
 
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index bcb5e72..07cc3d0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -84,7 +84,7 @@
      */
     @Nullable
     public static PackageInfo generate(AndroidPackage pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
             long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
@@ -105,7 +105,7 @@
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
      */
     private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
-            @PackageManager.PackageInfoFlags long flags, long firstInstallTime,
+            @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
             long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
             @Nullable ApexInfo apexInfo, @Nullable PackageStateInternal pkgSetting) {
         ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
@@ -209,7 +209,7 @@
      */
     @Nullable
     public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
-            @PackageManager.ApplicationInfoFlags long flags, @NonNull PackageUserState state,
+            @PackageManager.ApplicationInfoFlagsBits long flags, @NonNull PackageUserState state,
             int userId, @Nullable PackageStateInternal pkgSetting) {
         if (pkg == null) {
             return null;
@@ -255,7 +255,7 @@
      */
     @Nullable
     public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
     }
@@ -265,7 +265,7 @@
      */
     @Nullable
     private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (a == null) return null;
@@ -291,7 +291,7 @@
      */
     @Nullable
     public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlags long flags, PackageUserState state, int userId,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
     }
@@ -301,7 +301,7 @@
      */
     @Nullable
     private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (s == null) return null;
@@ -326,7 +326,7 @@
      */
     @Nullable
     public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlags long flags, PackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
             @NonNull ApplicationInfo applicationInfo, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (p == null) return null;
@@ -353,7 +353,7 @@
      */
     @Nullable
     public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
-            AndroidPackage pkg, @PackageManager.ComponentInfoFlags long flags, int userId,
+            AndroidPackage pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (i == null) return null;
 
@@ -381,7 +381,7 @@
     //  PackageStateInternal os that checkUseInstalledOrHidden filter can apply
     @Nullable
     public static PermissionInfo generatePermissionInfo(ParsedPermission p,
-            @PackageManager.ComponentInfoFlags long flags) {
+            @PackageManager.ComponentInfoFlagsBits long flags) {
         // TODO(b/135203078): Remove null checks and make all usages @NonNull
         if (p == null) return null;
 
@@ -391,7 +391,7 @@
 
     @Nullable
     public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
-            @PackageManager.ComponentInfoFlags long flags) {
+            @PackageManager.ComponentInfoFlagsBits long flags) {
         if (pg == null) return null;
 
         // For now, permissions don't have state-adjustable fields; return directly
@@ -400,7 +400,7 @@
 
     @Nullable
     public static ArrayMap<String, ProcessInfo> generateProcessInfo(
-            Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlags long flags) {
+            Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlagsBits long flags) {
         if (procs == null) {
             return null;
         }
@@ -423,7 +423,7 @@
      */
     public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
             PackageStateInternal pkgSetting, PackageUserState state,
-            @PackageManager.PackageInfoFlags long flags) {
+            @PackageManager.PackageInfoFlagsBits long flags) {
         // Returns false if the package is hidden system app until installed.
         if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
                 && !state.isInstalled()
@@ -628,8 +628,8 @@
          */
         @Nullable
         public ApplicationInfo generate(AndroidPackage pkg,
-                @PackageManager.ApplicationInfoFlags long flags, PackageUserState state, int userId,
-                @Nullable PackageStateInternal pkgSetting) {
+                @PackageManager.ApplicationInfoFlagsBits long flags, PackageUserState state,
+                int userId, @Nullable PackageStateInternal pkgSetting) {
             ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
             if (appInfo != null) {
                 return appInfo;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e207ff1..8643b5f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -103,12 +103,12 @@
     private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
     private static final boolean DEBUG = false;
 
-    @PackageManager.ResolveInfoFlags
+    @PackageManager.ResolveInfoFlagsBits
     private static final int DEFAULT_INTENT_QUERY_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                     | PackageManager.MATCH_UNINSTALLED_PACKAGES;
 
-    @PackageManager.PackageInfoFlags
+    @PackageManager.PackageInfoFlagsBits
     private static final int DEFAULT_PACKAGE_INFO_QUERY_FLAGS =
             PackageManager.MATCH_UNINSTALLED_PACKAGES
                     | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
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 471f38a..25147d0 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
@@ -391,7 +391,7 @@
      */
     @ApprovalLevel
     int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
-            @PackageManager.ResolveInfoFlags long resolveInfoFlags, @UserIdInt int userId);
+            @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags, @UserIdInt int userId);
 
     /**
      * @return the domain verification set ID for the given package, or null if the ID is
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 661e67d..111087f 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
@@ -1721,7 +1721,7 @@
 
     @Override
     public int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting,
-            @NonNull Intent intent, @PackageManager.ResolveInfoFlags long resolveInfoFlags,
+            @NonNull Intent intent, @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags,
             @UserIdInt int userId) {
         String packageName = pkgSetting.getPackageName();
         if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 12cce0d..058b605 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -49,7 +49,7 @@
     }
 
     public static boolean isDomainVerificationIntent(Intent intent,
-            @PackageManager.ResolveInfoFlags long resolveInfoFlags) {
+            @PackageManager.ResolveInfoFlagsBits long resolveInfoFlags) {
         if (!intent.isWebIntent()) {
             return false;
         }
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 51bd745..9127484 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -19,6 +19,7 @@
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
 
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.view.Display;
 
 /**
  * Used to store power related requests to every display in a
@@ -33,9 +34,10 @@
 
     private final DisplayPowerRequest mDisplayPowerRequest;
     private final boolean mSupportsSandman;
+    private final int mGroupId;
 
-    // True if DisplayManagerService has applied all the latest display states that were
-    // requested for this group
+    // True if DisplayManagerService has applied all the latest display states that were requested
+    // for this group
     private boolean mReady;
     // True if this group is in the process of powering on
     private boolean mPoweringOn;
@@ -49,8 +51,9 @@
     private long mLastUserActivityTime;
     private long mLastUserActivityTimeNoChangeLights;
 
-    PowerGroup(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
+    PowerGroup(int groupId, DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready,
             boolean supportsSandman) {
+        this.mGroupId = groupId;
         this.mDisplayPowerRequest = displayPowerRequest;
         this.mWakefulness = wakefulness;
         this.mReady = ready;
@@ -58,6 +61,7 @@
     }
 
     PowerGroup() {
+        this.mGroupId = Display.DEFAULT_DISPLAY_GROUP;
         this.mDisplayPowerRequest = new DisplayPowerRequest();
         this.mWakefulness = WAKEFULNESS_AWAKE;
         this.mReady = false;
@@ -72,6 +76,10 @@
         return mWakefulness;
     }
 
+    int getGroupId() {
+        return mGroupId;
+    }
+
     /**
      * Sets the {@code wakefulness} value for this {@link PowerGroup}.
      *
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 29166b3..1455326 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -104,7 +104,6 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.Preconditions;
@@ -117,7 +116,6 @@
 import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
-import com.android.server.display.DisplayGroup;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
 import com.android.server.policy.WindowManagerPolicy;
@@ -619,10 +617,6 @@
     @GuardedBy("mLock")
     private final SparseArray<PowerGroup> mPowerGroups = new SparseArray<>();
 
-    // A cached array of DisplayGroup Ids.
-    @GuardedBy("mLock")
-    private int[] mDisplayGroupIds;
-
     // We are currently in the middle of a batch change of uids.
     private boolean mUidsChanging;
 
@@ -665,13 +659,13 @@
                 // For now, only the default group supports sandman (dream/AOD).
                 final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP;
                 final PowerGroup powerGroup = new PowerGroup(
+                        groupId,
                         new DisplayPowerRequest(),
                         getGlobalWakefulnessLocked(),
                         /* ready= */ false,
                         supportsSandman);
                 mPowerGroups.append(groupId, powerGroup);
-                mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId);
-                onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId);
+                onPowerGroupEventLocked(DISPLAY_GROUP_ADDED, powerGroup);
             }
         }
 
@@ -682,20 +676,22 @@
                     Slog.wtf(TAG, "Tried to remove default display group: " + groupId);
                     return;
                 }
-                mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId);
                 if (!mPowerGroups.contains(groupId)) {
                     Slog.e(TAG, "Tried to remove non-existent group:" + groupId);
                     return;
                 }
-                mPowerGroups.delete(groupId);
-                onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId);
+                onPowerGroupEventLocked(DISPLAY_GROUP_REMOVED, mPowerGroups.get(groupId));
             }
         }
 
         @Override
         public void onDisplayGroupChanged(int groupId) {
             synchronized (mLock) {
-                onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId);
+                if (!mPowerGroups.contains(groupId)) {
+                    Slog.e(TAG, "Tried to change non-existent group: " + groupId);
+                    return;
+                }
+                onPowerGroupEventLocked(DISPLAY_GROUP_CHANGED, mPowerGroups.get(groupId));
             }
         }
     }
@@ -1150,7 +1146,7 @@
 
                 updatePowerStateLocked();
                 if (sQuiescent) {
-                    sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP,
+                    sleepDisplayGroupNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
                             mClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
@@ -1169,7 +1165,6 @@
             mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
             mAttentionDetector.systemReady(mContext);
             mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup());
-            mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP};
             mDisplayGroupPowerChangeListener = new DisplayGroupPowerChangeListener();
             mDisplayManagerInternal.registerDisplayGroupListener(mDisplayGroupPowerChangeListener);
 
@@ -1506,10 +1501,10 @@
                 opPackageName = wakeLock.mPackageName;
                 opUid = wakeLock.mOwnerUid;
             }
-            for (int id : mDisplayGroupIds) {
-                wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
-                        PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
-                        opUid, opPackageName, opUid);
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                wakeDisplayGroupNoUpdateLocked(mPowerGroups.valueAt(idx), mClock.uptimeMillis(),
+                        PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, opUid, opPackageName,
+                        opUid);
             }
         }
     }
@@ -1745,7 +1740,8 @@
             if (groupId == Display.INVALID_DISPLAY_GROUP) {
                 return;
             }
-            if (userActivityNoUpdateLocked(groupId, eventTime, event, flags, uid)) {
+            if (userActivityNoUpdateLocked(mPowerGroups.get(groupId), eventTime, event, flags,
+                    uid)) {
                 updatePowerStateLocked();
             }
         }
@@ -1753,8 +1749,10 @@
 
     private void onUserAttention() {
         synchronized (mLock) {
-            if (userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
+            if (userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+                    mClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_ATTENTION,
+                    0 /* flags */,
                     Process.SYSTEM_UID)) {
                 updatePowerStateLocked();
             }
@@ -1764,8 +1762,9 @@
     @GuardedBy("mLock")
     private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
         boolean updatePowerState = false;
-        for (int id : mDisplayGroupIds) {
-            if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) {
+        for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+            if (userActivityNoUpdateLocked(mPowerGroups.valueAt(idx), eventTime, event, flags,
+                    uid)) {
                 updatePowerState = true;
             }
         }
@@ -1774,10 +1773,10 @@
     }
 
     @GuardedBy("mLock")
-    private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
-            int uid) {
+    private boolean userActivityNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+            int event, int flags, int uid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
+            Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + powerGroup.getGroupId()
                     + ", eventTime=" + eventTime + ", event=" + event
                     + ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
         }
@@ -1801,7 +1800,7 @@
                 mOverriddenTimeout = -1;
             }
 
-            final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+            final int wakefulness = powerGroup.getWakefulnessLocked();
             if (wakefulness == WAKEFULNESS_ASLEEP
                     || wakefulness == WAKEFULNESS_DOZING
                     || (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
@@ -1811,11 +1810,9 @@
             maybeUpdateForegroundProfileLastActivityLocked(eventTime);
 
             if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
-                if (eventTime
-                        > mPowerGroups.get(groupId).getLastUserActivityTimeNoChangeLightsLocked()
-                        && eventTime > mPowerGroups.get(groupId).getLastUserActivityTimeLocked()) {
-                    mPowerGroups.get(groupId).setLastUserActivityTimeNoChangeLightsLocked(
-                            eventTime);
+                if (eventTime > powerGroup.getLastUserActivityTimeNoChangeLightsLocked()
+                        && eventTime > powerGroup.getLastUserActivityTimeLocked()) {
+                    powerGroup.setLastUserActivityTimeNoChangeLightsLocked(eventTime);
                     mDirty |= DIRTY_USER_ACTIVITY;
                     if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                         mDirty |= DIRTY_QUIESCENT;
@@ -1824,8 +1821,8 @@
                     return true;
                 }
             } else {
-                if (eventTime > mPowerGroups.get(groupId).getLastUserActivityTimeLocked()) {
-                    mPowerGroups.get(groupId).setLastUserActivityTimeLocked(eventTime);
+                if (eventTime > powerGroup.getLastUserActivityTimeLocked()) {
+                    powerGroup.setLastUserActivityTimeLocked(eventTime);
                     mDirty |= DIRTY_USER_ACTIVITY;
                     if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                         mDirty |= DIRTY_QUIESCENT;
@@ -1850,16 +1847,17 @@
     private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
             String details, int uid, String opPackageName, int opUid) {
         synchronized (mLock) {
-            if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
-                    opPackageName, opUid)) {
+            if (wakeDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, reason,
+                    details, uid, opPackageName, opUid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
     @GuardedBy("mLock")
-    private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
+    private boolean wakeDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
             @WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
+        final int groupId = powerGroup.getGroupId();
         if (DEBUG_SPEW) {
             Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                     + ", groupId=" + groupId + ", uid=" + uid);
@@ -1869,7 +1867,7 @@
             return false;
         }
 
-        final int currentState = mPowerGroups.get(groupId).getWakefulnessLocked();
+        final int currentState = powerGroup.getWakefulnessLocked();
         if (currentState == WAKEFULNESS_AWAKE) {
             if (!mBootCompleted && sQuiescent) {
                 mDirty |= DIRTY_QUIESCENT;
@@ -1892,9 +1890,8 @@
             LatencyTracker.getInstance(mContext)
                     .onActionStart(ACTION_TURN_ON_SCREEN, String.valueOf(groupId));
 
-            setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
+            setWakefulnessLocked(powerGroup, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
                     opPackageName, details);
-            PowerGroup powerGroup = mPowerGroups.get(groupId);
             powerGroup.setLastPowerOnTimeLocked(eventTime);
             powerGroup.setIsPoweringOnLocked(true);
         } finally {
@@ -1907,19 +1904,20 @@
     private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,
             int uid) {
         synchronized (mLock) {
-            if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {
+            if (sleepDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, reason, flags,
+                    uid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
     @GuardedBy("mLock")
-    private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
-            int flags, int uid) {
+    private boolean sleepDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+            int reason, int flags, int uid) {
         if (DEBUG_SPEW) {
             Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
-                    + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags
-                    + ", uid=" + uid);
+                    + ", groupId=" + powerGroup.getGroupId() + ", reason=" + reason
+                    + ", flags=" + flags + ", uid=" + uid);
         }
 
         if (eventTime < mLastWakeTime
@@ -1929,7 +1927,7 @@
             return false;
         }
 
-        final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+        final int wakefulness = powerGroup.getWakefulnessLocked();
         if (!PowerManagerInternal.isInteractive(wakefulness)) {
             return false;
         }
@@ -1939,14 +1937,14 @@
             reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
                     Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
             Slog.i(TAG, "Powering off display group due to "
-                    + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId
-                    + ", uid= " + uid + ")...");
+                    + PowerManager.sleepReasonToString(reason)
+                    + " (groupId= " + powerGroup.getGroupId() + ", uid= " + uid + ")...");
 
-            mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
-            setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,
+            powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
+            setWakefulnessLocked(powerGroup, WAKEFULNESS_DOZING, eventTime, uid, reason,
                     /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
             if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
-                reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
+                reallySleepDisplayGroupNoUpdateLocked(powerGroup, eventTime, uid);
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1956,14 +1954,15 @@
 
     private void dreamDisplayGroup(int groupId, long eventTime, int uid) {
         synchronized (mLock) {
-            if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) {
+            if (dreamDisplayGroupNoUpdateLocked(mPowerGroups.get(groupId), eventTime, uid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
     @GuardedBy("mLock")
-    private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
+    private boolean dreamDisplayGroupNoUpdateLocked(final PowerGroup powerGroup, long eventTime,
+            int uid) {
         if (DEBUG_SPEW) {
             Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                     + ", uid=" + uid);
@@ -1976,11 +1975,12 @@
 
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup");
         try {
-            Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")...");
+            Slog.i(TAG, "Napping display group (groupId=" + powerGroup.getGroupId() + ", uid=" + uid
+                    + ")...");
 
-            mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
-            setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
-                    0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
+            powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ true);
+            setWakefulnessLocked(powerGroup, WAKEFULNESS_DREAMING, eventTime, uid,
+                    /* reason= */0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
 
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1989,7 +1989,8 @@
     }
 
     @GuardedBy("mLock")
-    private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) {
+    private boolean reallySleepDisplayGroupNoUpdateLocked(final PowerGroup powerGroup,
+            long eventTime, int uid) {
         if (DEBUG_SPEW) {
             Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                     + ", uid=" + uid);
@@ -1997,16 +1998,18 @@
 
         if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP
                 || !mBootCompleted || !mSystemReady
-                || mPowerGroups.get(groupId).getWakefulnessLocked()
+                || powerGroup.getWakefulnessLocked()
                 == WAKEFULNESS_ASLEEP) {
             return false;
         }
 
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup");
         try {
-            Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid + ")...");
+            Slog.i(TAG,
+                    "Sleeping display group (groupId=" + powerGroup.getGroupId() + ", uid=" + uid
+                            + ")...");
 
-            setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
+            setWakefulnessLocked(powerGroup, WAKEFULNESS_ASLEEP, eventTime, uid,
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,  /* opUid= */ 0,
                     /* opPackageName= */ null, /* details= */ null);
         } finally {
@@ -2019,14 +2022,21 @@
     @GuardedBy("mLock")
     void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
             int opUid, String opPackageName, String details) {
-        if (mPowerGroups.get(groupId).setWakefulnessLocked(wakefulness)) {
+        setWakefulnessLocked(mPowerGroups.get(groupId), wakefulness, eventTime, uid, reason, opUid,
+                opPackageName, details);
+    }
+
+    @GuardedBy("mLock")
+    private void setWakefulnessLocked(final PowerGroup powerGroup, int wakefulness, long eventTime,
+            int uid, int reason, int opUid, String opPackageName, String details) {
+        if (powerGroup.setWakefulnessLocked(wakefulness)) {
             mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
             setGlobalWakefulnessLocked(getGlobalWakefulnessLocked(),
                     eventTime, reason, uid, opUid, opPackageName, details);
             if (wakefulness == WAKEFULNESS_AWAKE) {
                 // Kick user activity to prevent newly awake group from timing out instantly.
-                userActivityNoUpdateLocked(
-                        groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+                userActivityNoUpdateLocked(powerGroup, eventTime,
+                        PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
             }
         }
     }
@@ -2138,9 +2148,9 @@
     }
 
     /**
-     * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}.
+     * Returns the amalgamated wakefulness of all {@link PowerGroup PowerGroups}.
      *
-     * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered
+     * <p>This will be the highest wakeful state of all {@link PowerGroup PowerGroups}; ordered
      * from highest to lowest:
      * <ol>
      *     <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE}
@@ -2171,14 +2181,18 @@
     }
 
     @GuardedBy("mLock")
-    void onDisplayGroupEventLocked(int event, int groupId) {
+    void onPowerGroupEventLocked(int event, PowerGroup powerGroup) {
+        final int groupId = powerGroup.getGroupId();
+        if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED) {
+            mPowerGroups.remove(groupId);
+        }
         final int oldWakefulness = getWakefulnessLocked();
         final int newWakefulness = getGlobalWakefulnessLocked();
 
         if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED
                 && newWakefulness == WAKEFULNESS_AWAKE) {
             // Kick user activity to prevent newly added group from timing out instantly.
-            userActivityNoUpdateLocked(groupId, mClock.uptimeMillis(),
+            userActivityNoUpdateLocked(powerGroup, mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_OTHER, /* flags= */ 0, Process.SYSTEM_UID);
         }
 
@@ -2380,13 +2394,13 @@
                 final long now = mClock.uptimeMillis();
                 if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                         dockedOnWirelessCharger)) {
-                    wakeDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
-                            PowerManager.WAKE_REASON_PLUGGED_IN,
+                    wakeDisplayGroupNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+                            now, PowerManager.WAKE_REASON_PLUGGED_IN,
                             "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
                 }
 
-                userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
+                userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), now,
                         PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
                 // only play charging sounds if boot is completed so charging sounds don't play
@@ -2487,8 +2501,8 @@
                 mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
             }
 
-            for (int groupId : mDisplayGroupIds) {
-                mPowerGroups.get(groupId).setWakeLockSummaryLocked(0);
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                mPowerGroups.valueAt(idx).setWakeLockSummaryLocked(0);
             }
 
             int invalidGroupWakeLockSummary = 0;
@@ -2498,17 +2512,18 @@
                 final Integer groupId = wakeLock.getDisplayGroupId();
                 // a wakelock with an invalid group ID should affect all groups
                 if (groupId == null || (groupId != Display.INVALID_DISPLAY_GROUP
-                        && !ArrayUtils.contains(mDisplayGroupIds, groupId))) {
+                        && !mPowerGroups.contains(groupId))) {
                     continue;
                 }
 
+                final PowerGroup powerGroup = mPowerGroups.get(groupId);
                 final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
                 mWakeLockSummary |= wakeLockFlags;
 
                 if (groupId != Display.INVALID_DISPLAY_GROUP) {
-                    int wakeLockSummary = mPowerGroups.get(groupId).getWakeLockSummaryLocked();
+                    int wakeLockSummary = powerGroup.getWakeLockSummaryLocked();
                     wakeLockSummary |= wakeLockFlags;
-                    mPowerGroups.get(groupId).setWakeLockSummaryLocked(wakeLockSummary);
+                    powerGroup.setWakeLockSummaryLocked(wakeLockSummary);
                 } else {
                     invalidGroupWakeLockSummary |= wakeLockFlags;
                 }
@@ -2521,12 +2536,12 @@
                 }
             }
 
-            for (int groupId : mDisplayGroupIds) {
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
                 final int wakeLockSummary = adjustWakeLockSummary(
-                        mPowerGroups.get(groupId).getWakefulnessLocked(),
-                        invalidGroupWakeLockSummary
-                                | mPowerGroups.get(groupId).getWakeLockSummaryLocked());
-                mPowerGroups.get(groupId).setWakeLockSummaryLocked(wakeLockSummary);
+                        powerGroup.getWakefulnessLocked(),
+                        invalidGroupWakeLockSummary | powerGroup.getWakeLockSummaryLocked());
+                powerGroup.setWakeLockSummaryLocked(wakeLockSummary);
             }
 
             mWakeLockSummary = adjustWakeLockSummary(getWakefulnessLocked(),
@@ -2684,14 +2699,14 @@
         final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
         long nextTimeout = -1;
         boolean hasUserActivitySummary = false;
-        for (int groupId : mDisplayGroupIds) {
+        for (int idx = 0; idx < mPowerGroups.size(); idx++) {
             int groupUserActivitySummary = 0;
             long groupNextTimeout = 0;
-            if (mPowerGroups.get(groupId).getWakefulnessLocked() != WAKEFULNESS_ASLEEP) {
-                final long lastUserActivityTime =
-                        mPowerGroups.get(groupId).getLastUserActivityTimeLocked();
+            final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+            if (powerGroup.getWakefulnessLocked() != WAKEFULNESS_ASLEEP) {
+                final long lastUserActivityTime = powerGroup.getLastUserActivityTimeLocked();
                 final long lastUserActivityTimeNoChangeLights =
-                        mPowerGroups.get(groupId).getLastUserActivityTimeNoChangeLightsLocked();
+                        powerGroup.getLastUserActivityTimeNoChangeLightsLocked();
                 if (lastUserActivityTime >= mLastWakeTime) {
                     groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
                     if (now < groupNextTimeout) {
@@ -2708,7 +2723,7 @@
                     groupNextTimeout = lastUserActivityTimeNoChangeLights + screenOffTimeout;
                     if (now < groupNextTimeout) {
                         final DisplayPowerRequest displayPowerRequest =
-                                mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
+                                powerGroup.getDisplayPowerRequestLocked();
                         if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
                                 || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
                             groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
@@ -2749,7 +2764,7 @@
                 }
 
                 if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
-                        && (mPowerGroups.get(groupId).getWakeLockSummaryLocked()
+                        && (powerGroup.getWakeLockSummaryLocked()
                         & WAKE_LOCK_STAY_AWAKE) == 0) {
                     groupNextTimeout = mAttentionDetector.updateUserActivity(groupNextTimeout,
                             screenDimDuration);
@@ -2764,12 +2779,11 @@
                 }
             }
 
-            mPowerGroups.get(groupId).setUserActivitySummaryLocked(groupUserActivitySummary);
+            powerGroup.setUserActivitySummaryLocked(groupUserActivitySummary);
 
             if (DEBUG_SPEW) {
-                Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + groupId
-                        + ", mWakefulness=" + wakefulnessToString(
-                        mPowerGroups.get(groupId).getWakefulnessLocked())
+                Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + powerGroup.getGroupId()
+                        + ", mWakefulness=" + wakefulnessToString(powerGroup.getWakefulnessLocked())
                         + ", mUserActivitySummary=0x" + Integer.toHexString(
                         groupUserActivitySummary)
                         + ", nextTimeout=" + TimeUtils.formatUptime(groupNextTimeout));
@@ -2880,12 +2894,11 @@
     }
 
     @GuardedBy("mLock")
-    private boolean isAttentiveTimeoutExpired(int groupId, long now) {
+    private boolean isAttentiveTimeoutExpired(final PowerGroup powerGroup, long now) {
         long attentiveTimeout = getAttentiveTimeoutLocked();
         // Attentive state only applies to the default display group.
-        return groupId == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
-                && now >= mPowerGroups.get(groupId).getLastUserActivityTimeLocked()
-                + attentiveTimeout;
+        return powerGroup.getGroupId() == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
+                && now >= powerGroup.getLastUserActivityTimeLocked() + attentiveTimeout;
     }
 
     /**
@@ -2990,28 +3003,32 @@
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
                 | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
                 | DIRTY_DOCK_STATE | DIRTY_ATTENTIVE | DIRTY_SETTINGS
-                | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
-            final long time = mClock.uptimeMillis();
-            for (int id : mDisplayGroupIds) {
-                if (mPowerGroups.get(id).getWakefulnessLocked() == WAKEFULNESS_AWAKE
-                        && isItBedTimeYetLocked(id)) {
-                    if (DEBUG_SPEW) {
-                        Slog.d(TAG, "updateWakefulnessLocked: Bed time for group " + id);
-                    }
-                    if (isAttentiveTimeoutExpired(id, time)) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Going to sleep now due to long user inactivity");
-                        }
-                        changed = sleepDisplayGroupNoUpdateLocked(id, time,
-                                PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
-                                PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
-                    } else if (shouldNapAtBedTimeLocked()) {
-                        changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
-                    } else {
-                        changed = sleepDisplayGroupNoUpdateLocked(id, time,
-                                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
-                    }
+                | DIRTY_SCREEN_BRIGHTNESS_BOOST)) == 0) {
+            return changed;
+        }
+        final long time = mClock.uptimeMillis();
+        for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+            final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+            if (!(powerGroup.getWakefulnessLocked() == WAKEFULNESS_AWAKE
+                    && isItBedTimeYetLocked(powerGroup))) {
+                continue;
+            }
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "updateWakefulnessLocked: Bed time for group "
+                        + powerGroup.getGroupId());
+            }
+            if (isAttentiveTimeoutExpired(powerGroup, time)) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Going to sleep now due to long user inactivity");
                 }
+                changed = sleepDisplayGroupNoUpdateLocked(powerGroup, time,
+                        PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
+                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+            } else if (shouldNapAtBedTimeLocked()) {
+                changed = dreamDisplayGroupNoUpdateLocked(powerGroup, time, Process.SYSTEM_UID);
+            } else {
+                changed = sleepDisplayGroupNoUpdateLocked(powerGroup, time,
+                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
             }
         }
         return changed;
@@ -3029,39 +3046,38 @@
     }
 
     /**
-     * Returns true if the DisplayGroup with the provided {@code groupId} should go to sleep now.
+     * Returns true if the provided {@link PowerGroup} should go to sleep now.
      * Also used when exiting a dream to determine whether we should go back to being fully awake or
      * else go to sleep for good.
      */
     @GuardedBy("mLock")
-    private boolean isItBedTimeYetLocked(int groupId) {
+    private boolean isItBedTimeYetLocked(PowerGroup powerGroup) {
         if (!mBootCompleted) {
             return false;
         }
 
         long now = mClock.uptimeMillis();
-        if (isAttentiveTimeoutExpired(groupId, now)) {
+        if (isAttentiveTimeoutExpired(powerGroup, now)) {
             return !isBeingKeptFromInattentiveSleepLocked();
         } else {
-            return !isBeingKeptAwakeLocked(groupId);
+            return !isBeingKeptAwakeLocked(powerGroup);
         }
     }
 
     /**
-     * Returns true if the DisplayGroup with the provided {@code groupId} is being kept awake by a
-     * wake lock, user activity or the stay on while powered setting.  We also keep the phone awake
-     * when the proximity sensor returns a positive result so that the device does not lock while in
-     * a phone call.  This function only controls whether the device will go to sleep or dream which
-     * is independent of whether it will be allowed to suspend.
+     * Returns true if the provided {@link PowerGroup} is being kept awake by a wake lock, user
+     * activity or the stay on while powered setting.  We also keep the phone awake when the
+     * proximity sensor returns a positive result so that the device does not lock while in a phone
+     * call. This function only controls whether the device will go to sleep or dream which is
+     * independent of whether it will be allowed to suspend.
      */
     @GuardedBy("mLock")
-    private boolean isBeingKeptAwakeLocked(int groupId) {
+    private boolean isBeingKeptAwakeLocked(final PowerGroup powerGroup) {
         return mStayOn
                 || mProximityPositive
-                || (mPowerGroups.get(groupId).getWakeLockSummaryLocked() & WAKE_LOCK_STAY_AWAKE)
-                != 0
-                || (mPowerGroups.get(groupId).getUserActivitySummaryLocked() & (
-                USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
+                || (powerGroup.getWakeLockSummaryLocked() & WAKE_LOCK_STAY_AWAKE) != 0
+                || (powerGroup.getUserActivitySummaryLocked() & (
+                        USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
                 || mScreenBrightnessBoostInProgress;
     }
 
@@ -3102,10 +3118,11 @@
     private void scheduleSandmanLocked() {
         if (!mSandmanScheduled) {
             mSandmanScheduled = true;
-            for (int id : mDisplayGroupIds) {
-                if (mPowerGroups.get(id).supportsSandmanLocked()) {
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+                if (powerGroup.supportsSandmanLocked()) {
                     Message msg = mHandler.obtainMessage(MSG_SANDMAN);
-                    msg.arg1 = id;
+                    msg.arg1 = powerGroup.getGroupId();
                     msg.setAsynchronous(true);
                     mHandler.sendMessage(msg);
                 }
@@ -3126,16 +3143,16 @@
         final int wakefulness;
         synchronized (mLock) {
             mSandmanScheduled = false;
-            if (!ArrayUtils.contains(mDisplayGroupIds, groupId)) {
+            if (!mPowerGroups.contains(groupId)) {
                 // Group has been removed.
                 return;
             }
-            wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
+            final PowerGroup powerGroup = mPowerGroups.get(groupId);
+            wakefulness = powerGroup.getWakefulnessLocked();
             if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
-                    mPowerGroups.get(groupId).isSandmanSummonedLocked()
-                    && mPowerGroups.get(groupId).isReadyLocked()) {
-                startDreaming = canDreamLocked(groupId) || canDozeLocked();
-                mPowerGroups.get(groupId).setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
+                    powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
+                startDreaming = canDreamLocked(powerGroup) || canDozeLocked();
+                powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
             } else {
                 startDreaming = false;
             }
@@ -3162,7 +3179,7 @@
 
         // Update dream state.
         synchronized (mLock) {
-            if (!ArrayUtils.contains(mDisplayGroupIds, groupId)) {
+            if (!mPowerGroups.contains(groupId)) {
                 // Group has been removed.
                 return;
             }
@@ -3179,19 +3196,20 @@
 
             // If preconditions changed, wait for the next iteration to determine
             // whether the dream should continue (or be restarted).
-            if (mPowerGroups.get(groupId).isSandmanSummonedLocked()
-                    || mPowerGroups.get(groupId).getWakefulnessLocked() != wakefulness) {
+            final PowerGroup powerGroup = mPowerGroups.get(groupId);
+            if (powerGroup.isSandmanSummonedLocked()
+                    || powerGroup.getWakefulnessLocked() != wakefulness) {
                 return; // wait for next cycle
             }
 
             // Determine whether the dream should continue.
             long now = mClock.uptimeMillis();
             if (wakefulness == WAKEFULNESS_DREAMING) {
-                if (isDreaming && canDreamLocked(groupId)) {
+                if (isDreaming && canDreamLocked(powerGroup)) {
                     if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                             && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                     - mDreamsBatteryLevelDrainCutoffConfig
-                            && !isBeingKeptAwakeLocked(groupId)) {
+                            && !isBeingKeptAwakeLocked(powerGroup)) {
                         // If the user activity timeout expired and the battery appears
                         // to be draining faster than it is charging then stop dreaming
                         // and go to sleep.
@@ -3206,13 +3224,14 @@
                 }
 
                 // Dream has ended or will be stopped.  Update the power state.
-                if (isItBedTimeYetLocked(groupId)) {
-                    final int flags = isAttentiveTimeoutExpired(groupId, now)
+                if (isItBedTimeYetLocked(powerGroup)) {
+                    final int flags = isAttentiveTimeoutExpired(powerGroup, now)
                             ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
-                    sleepDisplayGroupNoUpdateLocked(groupId, now,
+                    sleepDisplayGroupNoUpdateLocked(powerGroup, now,
                             PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
                 } else {
-                    wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
+                    wakeDisplayGroupNoUpdateLocked(powerGroup, now,
+                            PowerManager.WAKE_REASON_UNKNOWN,
                             "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
                             mContext.getOpPackageName(), Process.SYSTEM_UID);
                 }
@@ -3223,7 +3242,7 @@
                 }
 
                 // Doze has ended or will be stopped.  Update the power state.
-                reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID);
+                reallySleepDisplayGroupNoUpdateLocked(powerGroup, now, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
         }
@@ -3238,21 +3257,19 @@
      * Returns true if the {@code groupId} is allowed to dream in its current state.
      */
     @GuardedBy("mLock")
-    private boolean canDreamLocked(int groupId) {
-        final DisplayPowerRequest displayPowerRequest =
-                mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
+    private boolean canDreamLocked(final PowerGroup powerGroup) {
+        final DisplayPowerRequest displayPowerRequest = powerGroup.getDisplayPowerRequestLocked();
         if (!mBootCompleted
                 || getWakefulnessLocked() != WAKEFULNESS_DREAMING
                 || !mDreamsSupportedConfig
                 || !mDreamsEnabledSetting
                 || !displayPowerRequest.isBrightOrDim()
                 || displayPowerRequest.isVr()
-                || (mPowerGroups.get(groupId).getUserActivitySummaryLocked() & (
-                USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM
-                        | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
+                || (powerGroup.getUserActivitySummaryLocked() & (USER_ACTIVITY_SCREEN_BRIGHT
+                | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
             return false;
         }
-        if (!isBeingKeptAwakeLocked(groupId)) {
+        if (!isBeingKeptAwakeLocked(powerGroup)) {
             if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
                 return false;
             }
@@ -3302,10 +3319,12 @@
                 }
             }
 
-            for (final int groupId : mDisplayGroupIds) {
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+                final int groupId = powerGroup.getGroupId();
                 final DisplayPowerRequest displayPowerRequest =
-                        mPowerGroups.get(groupId).getDisplayPowerRequestLocked();
-                displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId);
+                        powerGroup.getDisplayPowerRequestLocked();
+                displayPowerRequest.policy = getDesiredScreenPolicyLocked(powerGroup);
 
                 // Determine appropriate screen brightness and auto-brightness adjustments.
                 final boolean autoBrightness;
@@ -3334,7 +3353,7 @@
 
                 if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                     displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
-                    if ((mPowerGroups.get(groupId).getWakeLockSummaryLocked() & WAKE_LOCK_DRAW) != 0
+                    if ((powerGroup.getWakeLockSummaryLocked() & WAKE_LOCK_DRAW) != 0
                             && !mDrawWakeLockOverrideFromSidekick) {
                         if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
                             displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
@@ -3361,11 +3380,11 @@
                             + ", policy=" + policyToString(displayPowerRequest.policy)
                             + ", mWakefulness="
                             + PowerManagerInternal.wakefulnessToString(
-                            mPowerGroups.get(groupId).getWakefulnessLocked())
+                            powerGroup.getWakefulnessLocked())
                             + ", mWakeLockSummary=0x" + Integer.toHexString(
-                            mPowerGroups.get(groupId).getWakeLockSummaryLocked())
+                            powerGroup.getWakeLockSummaryLocked())
                             + ", mUserActivitySummary=0x" + Integer.toHexString(
-                            mPowerGroups.get(groupId).getUserActivitySummaryLocked())
+                            powerGroup.getUserActivitySummaryLocked())
                             + ", mBootCompleted=" + mBootCompleted
                             + ", screenBrightnessOverride="
                             + displayPowerRequest.screenBrightnessOverride
@@ -3376,16 +3395,15 @@
                             + ", sQuiescent=" + sQuiescent);
                 }
 
-                final boolean displayReadyStateChanged =
-                        mPowerGroups.get(groupId).setReadyLocked(ready);
-                final boolean poweringOn = mPowerGroups.get(groupId).isPoweringOnLocked();
+                final boolean displayReadyStateChanged = powerGroup.setReadyLocked(ready);
+                final boolean poweringOn = powerGroup.isPoweringOnLocked();
                 if (ready && displayReadyStateChanged && poweringOn
-                        && mPowerGroups.get(groupId).getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
-                    mPowerGroups.get(groupId).setIsPoweringOnLocked(false);
+                        && powerGroup.getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+                    powerGroup.setIsPoweringOnLocked(false);
                     LatencyTracker.getInstance(mContext).onActionEnd(ACTION_TURN_ON_SCREEN);
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
                     final int latencyMs = (int) (mClock.uptimeMillis()
-                            - mPowerGroups.get(groupId).getLastPowerOnTimeLocked());
+                            - powerGroup.getLastPowerOnTimeLocked());
                     if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
                         Slog.w(TAG, "Screen on took " + latencyMs + " ms");
                     }
@@ -3431,8 +3449,12 @@
     @VisibleForTesting
     @GuardedBy("mLock")
     int getDesiredScreenPolicyLocked(int groupId) {
-        final int wakefulness = mPowerGroups.get(groupId).getWakefulnessLocked();
-        final int wakeLockSummary = mPowerGroups.get(groupId).getWakeLockSummaryLocked();
+        return getDesiredScreenPolicyLocked(mPowerGroups.get(groupId));
+    }
+
+    int getDesiredScreenPolicyLocked(final PowerGroup powerGroup) {
+        final int wakefulness = powerGroup.getWakefulnessLocked();
+        final int wakeLockSummary = powerGroup.getWakeLockSummaryLocked();
         if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
             return DisplayPowerRequest.POLICY_OFF;
         } else if (wakefulness == WAKEFULNESS_DOZING) {
@@ -3455,8 +3477,7 @@
 
         if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
                 || !mBootCompleted
-                || (mPowerGroups.get(groupId).getUserActivitySummaryLocked()
-                & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                || (powerGroup.getUserActivitySummaryLocked() & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                 || mScreenBrightnessBoostInProgress) {
             return DisplayPowerRequest.POLICY_BRIGHT;
         }
@@ -3490,8 +3511,9 @@
                 mProximityPositive = false;
                 mInterceptedPowerKeyForProximity = false;
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
-                userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
-                        PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+                userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
+                        mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                        0 /* flags */, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
         }
@@ -3551,8 +3573,8 @@
         final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
         final boolean autoSuspend = !needDisplaySuspendBlocker;
         boolean interactive = false;
-        for (int id : mDisplayGroupIds) {
-            interactive |= mPowerGroups.get(id).getDisplayPowerRequestLocked().isBrightOrDim();
+        for (int idx = 0; idx < mPowerGroups.size() && !interactive; idx++) {
+            interactive = mPowerGroups.valueAt(idx).getDisplayPowerRequestLocked().isBrightOrDim();
         }
 
         // Disable auto-suspend if needed.
@@ -3635,9 +3657,9 @@
             return true;
         }
 
-        for (int id : mDisplayGroupIds) {
+        for (int idx = 0; idx < mPowerGroups.size(); idx++) {
             final DisplayPowerRequest displayPowerRequest =
-                    mPowerGroups.get(id).getDisplayPowerRequestLocked();
+                    mPowerGroups.valueAt(idx).getDisplayPowerRequestLocked();
             if (displayPowerRequest.isBrightOrDim()) {
                 // If we asked for the screen to be on but it is off due to the proximity
                 // sensor then we may suspend but only if the configuration allows it.
@@ -4077,7 +4099,7 @@
             mScreenBrightnessBoostInProgress = true;
             mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
 
-            userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, eventTime,
+            userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime,
                     PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
             updatePowerStateLocked();
         }
@@ -4201,9 +4223,9 @@
                 mForceSuspendActive = true;
                 // Place the system in an non-interactive state
                 boolean updatePowerState = false;
-                for (int id : mDisplayGroupIds) {
-                    updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
-                            PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+                for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                    updatePowerState |= sleepDisplayGroupNoUpdateLocked(mPowerGroups.valueAt(idx),
+                            mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
                 }
                 if (updatePowerState) {
@@ -4504,16 +4526,17 @@
             }
 
             pw.println("Display Group User Activity:");
-            for (int id : mDisplayGroupIds) {
-                pw.println("  displayGroupId=" + id);
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
+                pw.println("  displayGroupId=" + powerGroup.getGroupId());
                 pw.println("  userActivitySummary=0x" + Integer.toHexString(
-                        mPowerGroups.get(id).getUserActivitySummaryLocked()));
+                        powerGroup.getUserActivitySummaryLocked()));
                 pw.println("  lastUserActivityTime=" + TimeUtils.formatUptime(
-                        mPowerGroups.get(id).getLastUserActivityTimeLocked()));
+                        powerGroup.getLastUserActivityTimeLocked()));
                 pw.println("  lastUserActivityTimeNoChangeLights=" + TimeUtils.formatUptime(
-                        mPowerGroups.get(id).getLastUserActivityTimeNoChangeLightsLocked()));
+                        powerGroup.getLastUserActivityTimeNoChangeLightsLocked()));
                 pw.println("  mWakeLockSummary=0x" + Integer.toHexString(
-                        mPowerGroups.get(id).getWakeLockSummaryLocked()));
+                        powerGroup.getWakeLockSummaryLocked()));
             }
 
             wcd = mWirelessChargerDetector;
@@ -4601,12 +4624,13 @@
             proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
             proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
 
-            for (int id : mDisplayGroupIds) {
+            for (int idx = 0; idx < mPowerGroups.size(); idx++) {
+                final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
                 final long userActivityToken = proto.start(
                         PowerManagerServiceDumpProto.USER_ACTIVITY);
-                proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID, id);
-                final long userActivitySummary =
-                        mPowerGroups.get(id).getUserActivitySummaryLocked();
+                proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID,
+                        powerGroup.getGroupId());
+                final long userActivitySummary = powerGroup.getUserActivitySummaryLocked();
                 proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
                         (userActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
                 proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
@@ -4615,10 +4639,10 @@
                         (userActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
                 proto.write(
                         PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_MS,
-                        mPowerGroups.get(id).getLastUserActivityTimeLocked());
+                        powerGroup.getLastUserActivityTimeLocked());
                 proto.write(
                         PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
-                        mPowerGroups.get(id).getLastUserActivityTimeNoChangeLightsLocked());
+                        powerGroup.getLastUserActivityTimeNoChangeLightsLocked());
                 proto.end(userActivityToken);
             }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ba96590..023db13 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1291,9 +1291,6 @@
         // This is used to block background activity launch even if the app is still
         // visible to user after user clicking home button.
         final int appSwitchState = mService.getBalAppSwitchesState();
-        final boolean appSwitchAllowed = appSwitchState == APP_SWITCH_ALLOW;
-        final boolean appSwitchAllowedOrFg =
-                appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
 
         // don't abort if the callingUid has a visible window or is a persistent system process
         final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
@@ -1306,6 +1303,8 @@
 
         // Normal apps with visible app window will be allowed to start activity if app switching
         // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+        final boolean appSwitchAllowedOrFg =
+                appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
         if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
                 && callingUidHasAnyVisibleWindow)
                 || isCallingUidPersistentSystemProcess) {
@@ -1435,7 +1434,7 @@
         // don't abort if the callerApp or other processes of that uid are allowed in any way
         if (callerApp != null) {
             // first check the original calling process
-            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
                             + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
@@ -1449,7 +1448,7 @@
                 for (int i = uidProcesses.size() - 1; i >= 0; i--) {
                     final WindowProcessController proc = uidProcesses.valueAt(i);
                     if (proc != callerApp
-                            && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+                            && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
                         if (DEBUG_ACTIVITY_STARTS) {
                             Slog.d(TAG,
                                     "Background activity start allowed: process " + proc.getPid()
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 71a10df..0afd872 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -20,6 +20,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -70,13 +72,13 @@
     }
 
     boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
-            boolean appSwitchAllowed, boolean isCheckingForFgsStart,
+            int appSwitchState, boolean isCheckingForFgsStart,
             boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
             long lastStopAppSwitchesTime, long lastActivityLaunchTime,
             long lastActivityFinishTime) {
         // If app switching is not allowed, we ignore all the start activity grace period
         // exception so apps cannot start itself in onPause() after pressing home button.
-        if (appSwitchAllowed) {
+        if (appSwitchState == APP_SWITCH_ALLOW) {
             // Allow if any activity in the caller has either started or finished very recently, and
             // it must be started or finished after last stop app switches time.
             final long now = SystemClock.uptimeMillis();
@@ -111,7 +113,8 @@
             return true;
         }
         // Allow if the caller has an activity in any foreground task.
-        if (appSwitchAllowed && hasActivityInVisibleTask) {
+        if (hasActivityInVisibleTask
+                && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "[Process(" + pid
                         + ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3864c8f..eb68952 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5885,6 +5885,13 @@
                 rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                         notifyClients);
             });
+            if (mTransitionController.isCollecting()
+                    && mWallpaperController.getWallpaperTarget() != null) {
+                // Also update wallpapers so that their requestedVisibility immediately reflects
+                // the changes to activity visibility.
+                // TODO(b/206005136): Move visibleRequested logic up to WindowToken.
+                mWallpaperController.adjustWallpaperWindows();
+            }
         } finally {
             mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
             mInEnsureActivitiesVisible = false;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 94a175c..8a2d116 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -251,15 +251,47 @@
      */
     boolean activityBlockedFromFinish(ActivityRecord activity) {
         final Task task = activity.getTask();
-        if (activity == task.getRootActivity()
-                && activity == task.getTopNonFinishingActivity()
-                && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                && isRootTask(task)) {
-            Slog.i(TAG, "Not finishing task in lock task mode");
-            showLockTaskToast();
-            return true;
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) {
+            return false;
         }
-        return false;
+
+        final ActivityRecord taskTop = task.getTopNonFinishingActivity();
+        final ActivityRecord taskRoot = task.getRootActivity();
+        // If task has more than one Activity, verify if there's only adjacent TaskFragments that
+        // should be finish together in the Task.
+        if (activity != taskRoot || activity != taskTop) {
+            final TaskFragment taskFragment = activity.getTaskFragment();
+            final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+            if (taskFragment.asTask() != null
+                    || !taskFragment.isDelayLastActivityRemoval()
+                    || adjacentTaskFragment == null) {
+                // Don't block activity from finishing if the TaskFragment don't have any adjacent
+                // TaskFragment, or it won't finish together with its adjacent TaskFragment.
+                return false;
+            }
+
+            final boolean hasOtherActivityInTaskFragment =
+                    taskFragment.getActivity(a -> !a.finishing && a != activity) != null;
+            if (hasOtherActivityInTaskFragment) {
+                // Don't block activity from finishing if there's other Activity in the same
+                // TaskFragment.
+                return false;
+            }
+
+            final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing
+                    && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null;
+            if (hasOtherActivityInTask) {
+                // Do not block activity from finishing if there are another running activities
+                // after the current and adjacent TaskFragments are removed. Note that we don't
+                // check activities in adjacent TaskFragment because it will be finished together
+                // with TaskFragment regardless of numbers of activities.
+                return false;
+            }
+        }
+
+        Slog.i(TAG, "Not finishing task in lock task mode");
+        showLockTaskToast();
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b54208d..9ad30da 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -255,25 +255,27 @@
         mDestRotatedBounds = null;
         mPipTransaction = null;
         final Rect areaBounds = taskArea.getBounds();
-        if (pipTx != null) {
+        if (pipTx != null && pipTx.mPosition != null) {
             // The transaction from recents animation is in old rotation. So the position needs to
             // be rotated.
-            float dx = pipTx.mPositionX;
-            float dy = pipTx.mPositionY;
+            float dx = pipTx.mPosition.x;
+            float dy = pipTx.mPosition.y;
             final Matrix matrix = pipTx.getMatrix();
             if (pipTx.mRotation == 90) {
-                dx = pipTx.mPositionY;
-                dy = areaBounds.right - pipTx.mPositionX;
+                dx = pipTx.mPosition.y;
+                dy = areaBounds.right - pipTx.mPosition.x;
                 matrix.postRotate(-90);
             } else if (pipTx.mRotation == -90) {
-                dx = areaBounds.bottom - pipTx.mPositionY;
-                dy = pipTx.mPositionX;
+                dx = areaBounds.bottom - pipTx.mPosition.y;
+                dy = pipTx.mPosition.x;
                 matrix.postRotate(90);
             }
             matrix.postTranslate(dx, dy);
             final SurfaceControl leash = pinnedTask.getSurfaceControl();
-            t.setMatrix(leash, matrix, new float[9])
-                    .setCornerRadius(leash, pipTx.mCornerRadius);
+            t.setMatrix(leash, matrix, new float[9]);
+            if (pipTx.hasCornerRadiusSet()) {
+                t.setCornerRadius(leash, pipTx.mCornerRadius);
+            }
             Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
             return;
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 38e3e3a..f97a48b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -263,13 +263,6 @@
                     "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
             final long token = Binder.clearCallingIdentity();
             try {
-                synchronized (mService.getWindowManagerLock()) {
-                    // Remove all new task targets.
-                    for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) {
-                        removeTaskInternal(mPendingNewTaskTargets.get(i));
-                    }
-                }
-
                 // Note, the callback will handle its own synchronization, do not lock on WM lock
                 // prior to calling the callback
                 mCallbacks.onAnimationFinished(moveHomeToTop
@@ -759,7 +752,7 @@
         // the task-id with the leaf id.
         final Task leafTask = task.getTopLeafTask();
         int taskId = leafTask.mTaskId;
-        TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
+        TaskAnimationAdapter adapter = addAnimation(task,
                 !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
         mPendingNewTaskTargets.add(taskId);
         return adapter.createRemoteAnimationTarget(taskId);
@@ -1012,6 +1005,7 @@
             taskAdapter.onCleanup();
         }
         // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+        mPendingNewTaskTargets.clear();
         mPendingTaskAppears.clear();
 
         for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c845dca..97cb512 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -240,7 +240,7 @@
     /**
      * Whether to delay the last activity of TaskFragment being immediately removed while finishing.
      * This should only be set on a embedded TaskFragment, where the organizer can have the
-     * opportunity to perform other actions or animations.
+     * opportunity to perform animations and finishing the adjacent TaskFragment.
      */
     private boolean mDelayLastActivityRemoval;
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3974747..dae004d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -365,6 +365,13 @@
                 t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
                 t.setCornerRadius(targetLeash, 0);
                 t.setShadowRadius(targetLeash, 0);
+                // The bounds sent to the transition is always a real bounds. This means we lose
+                // information about "null" bounds (inheriting from parent). Core will fix-up
+                // non-organized window surface bounds; however, since Core can't touch organized
+                // surfaces, add the "inherit from parent" restoration here.
+                if (target.isOrganized() && target.matchParentBounds()) {
+                    t.setWindowCrop(targetLeash, -1, -1);
+                }
                 displays.add(target.getDisplayContent());
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index cc52713..7956a11 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -173,7 +173,7 @@
 
     @VisibleForTesting
     class WindowContextListenerImpl implements WindowContainerListener {
-        @NonNull private final IBinder mClientToken;
+        @NonNull private final IWindowToken mClientToken;
         private final int mOwnerUid;
         @NonNull private WindowContainer<?> mContainer;
         /**
@@ -193,7 +193,7 @@
 
         private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
                 int ownerUid, @WindowType int type, @Nullable Bundle options) {
-            mClientToken = clientToken;
+            mClientToken = IWindowToken.Stub.asInterface(clientToken);
             mContainer = Objects.requireNonNull(container);
             mOwnerUid = ownerUid;
             mType = type;
@@ -205,7 +205,7 @@
                 mDeathRecipient = deathRecipient;
             } catch (RemoteException e) {
                 ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, "
-                        + "container=%s", mClientToken, mContainer);
+                        + "container=%s", clientToken, mContainer);
             }
         }
 
@@ -228,17 +228,17 @@
         }
 
         private void register() {
+            final IBinder token = mClientToken.asBinder();
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + token);
             }
-            mListeners.putIfAbsent(mClientToken, this);
+            mListeners.putIfAbsent(token, this);
             mContainer.registerWindowContainerListener(this);
-            reportConfigToWindowTokenClient();
         }
 
         private void unregister() {
             mContainer.unregisterWindowContainerListener(this);
-            mListeners.remove(mClientToken);
+            mListeners.remove(mClientToken.asBinder());
         }
 
         private void clear() {
@@ -258,19 +258,24 @@
 
         private void reportConfigToWindowTokenClient() {
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
+            }
+            final DisplayContent dc = mContainer.getDisplayContent();
+            if (!dc.isReady()) {
+                // Do not report configuration when booting. The latest configuration will be sent
+                // when WindowManagerService#displayReady().
+                return;
             }
             // If the display of window context associated window container is suspended, don't
             // report the configuration update. Note that we still dispatch the configuration update
             // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
             // Service always receives #onConfigurationChanged callback regardless of display state.
-            if (!isWindowProviderService(mOptions)
-                    && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+            if (!isWindowProviderService(mOptions) && isSuspendedState(dc.getDisplayInfo().state)) {
                 mHasPendingConfiguration = true;
                 return;
             }
             final Configuration config = mContainer.getConfiguration();
-            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final int displayId = dc.getDisplayId();
             if (mLastReportedConfig == null) {
                 mLastReportedConfig = new Configuration();
             }
@@ -282,9 +287,8 @@
             mLastReportedConfig.setTo(config);
             mLastReportedDisplay = displayId;
 
-            IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
             try {
-                windowTokenClient.onConfigurationChanged(config, displayId);
+                mClientToken.onConfigurationChanged(config, displayId);
             } catch (RemoteException e) {
                 ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
             }
@@ -294,7 +298,7 @@
         @Override
         public void onRemoved() {
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
             }
             final WindowToken windowToken = mContainer.asWindowToken();
             if (windowToken != null && windowToken.isFromClient()) {
@@ -312,9 +316,8 @@
                 }
             }
             mDeathRecipient.unlinkToDeath();
-            IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
             try {
-                windowTokenClient.onWindowTokenRemoved();
+                mClientToken.onWindowTokenRemoved();
             } catch (RemoteException e) {
                 ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
             }
@@ -323,7 +326,7 @@
 
         @Override
         public String toString() {
-            return "WindowContextListenerImpl{clientToken=" + mClientToken + ", "
+            return "WindowContextListenerImpl{clientToken=" + mClientToken.asBinder() + ", "
                     + "container=" + mContainer + "}";
         }
 
@@ -337,11 +340,11 @@
             }
 
             void linkToDeath() throws RemoteException {
-                mClientToken.linkToDeath(this, 0);
+                mClientToken.asBinder().linkToDeath(this, 0);
             }
 
             void unlinkToDeath() {
-                mClientToken.unlinkToDeath(this, 0);
+                mClientToken.asBinder().unlinkToDeath(this, 0);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 525d84be..79dcbcb 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -33,6 +33,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -79,6 +80,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -785,6 +788,22 @@
                         null /* requiredPermission */, options);
                 break;
             }
+            case HIERARCHY_OP_TYPE_START_SHORTCUT: {
+                final Bundle launchOpts = hop.getLaunchOptions();
+                final String callingPackage = launchOpts.getString(
+                        WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
+                launchOpts.remove(
+                        WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE);
+
+                final LauncherAppsServiceInternal launcherApps = LocalServices.getService(
+                        LauncherAppsServiceInternal.class);
+
+                launcherApps.startShortcut(caller.mUid, caller.mPid, callingPackage,
+                        hop.getShortcutInfo().getPackage(), null /* default featureId */,
+                        hop.getShortcutInfo().getId(), null /* sourceBounds */, launchOpts,
+                        hop.getShortcutInfo().getUserId());
+                break;
+            }
             case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
                 final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
                 final WindowContainer newParent = hop.getNewParent() != null
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 90a5d69e..3ccb06c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -37,7 +37,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
 import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
@@ -513,19 +512,19 @@
      */
     @HotPath(caller = HotPath.START_SERVICE)
     public boolean areBackgroundFgsStartsAllowed() {
-        return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState() == APP_SWITCH_ALLOW,
+        return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
                 true /* isCheckingForFgsStart */);
     }
 
-    boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
-        return areBackgroundActivityStartsAllowed(appSwitchAllowed,
+    boolean areBackgroundActivityStartsAllowed(int appSwitchState) {
+        return areBackgroundActivityStartsAllowed(appSwitchState,
                 false /* isCheckingForFgsStart */);
     }
 
-    private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+    private boolean areBackgroundActivityStartsAllowed(int appSwitchState,
             boolean isCheckingForFgsStart) {
         return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
-                appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(),
+                appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
                 mInstrumentingWithBackgroundActivityStartPrivileges,
                 mAtm.getLastStopAppSwitchesTime(),
                 mLastActivityLaunchTime, mLastActivityFinishTime);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f72f2cc..f5eb0a7 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -35,6 +35,7 @@
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
         "com_android_server_ConsumerIrService.cpp",
+        "com_android_server_companion_virtual_InputController.cpp",
         "com_android_server_devicepolicy_CryptoTestHelper.cpp",
         "com_android_server_connectivity_Vpn.cpp",
         "com_android_server_gpu_GpuService.cpp",
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
new file mode 100644
index 0000000..43018a9
--- /dev/null
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputController"
+
+#include <android-base/unique_fd.h>
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/uinput.h>
+#include <math.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+enum class DeviceType {
+    KEYBOARD,
+    MOUSE,
+    TOUCHSCREEN,
+};
+
+enum class UinputAction {
+    RELEASE = 0,
+    PRESS = 1,
+    MOVE = 2,
+    CANCEL = 3,
+};
+
+static std::map<int, UinputAction> BUTTON_ACTION_MAPPING = {
+        {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
+        {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
+};
+
+static std::map<int, UinputAction> KEY_ACTION_MAPPING = {
+        {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
+        {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
+};
+
+static std::map<int, UinputAction> TOUCH_ACTION_MAPPING = {
+        {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
+        {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
+        {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
+        {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
+};
+
+// Button code mapping from https://source.android.com/devices/input/touch-devices
+static std::map<int, int> BUTTON_CODE_MAPPING = {
+        {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT},    {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
+        {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE}, {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
+        {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
+};
+
+// Tool type mapping from https://source.android.com/devices/input/touch-devices
+static std::map<int, int> TOOL_TYPE_MAPPING = {
+        {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
+        {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
+};
+
+// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
+static std::map<int, int> KEY_CODE_MAPPING = {
+        {AKEYCODE_0, KEY_0},
+        {AKEYCODE_1, KEY_1},
+        {AKEYCODE_2, KEY_2},
+        {AKEYCODE_3, KEY_3},
+        {AKEYCODE_4, KEY_4},
+        {AKEYCODE_5, KEY_5},
+        {AKEYCODE_6, KEY_6},
+        {AKEYCODE_7, KEY_7},
+        {AKEYCODE_8, KEY_8},
+        {AKEYCODE_9, KEY_9},
+        {AKEYCODE_A, KEY_A},
+        {AKEYCODE_B, KEY_B},
+        {AKEYCODE_C, KEY_C},
+        {AKEYCODE_D, KEY_D},
+        {AKEYCODE_E, KEY_E},
+        {AKEYCODE_F, KEY_F},
+        {AKEYCODE_G, KEY_G},
+        {AKEYCODE_H, KEY_H},
+        {AKEYCODE_I, KEY_I},
+        {AKEYCODE_J, KEY_J},
+        {AKEYCODE_K, KEY_K},
+        {AKEYCODE_L, KEY_L},
+        {AKEYCODE_M, KEY_M},
+        {AKEYCODE_N, KEY_N},
+        {AKEYCODE_O, KEY_O},
+        {AKEYCODE_P, KEY_P},
+        {AKEYCODE_Q, KEY_Q},
+        {AKEYCODE_R, KEY_R},
+        {AKEYCODE_S, KEY_S},
+        {AKEYCODE_T, KEY_T},
+        {AKEYCODE_U, KEY_U},
+        {AKEYCODE_V, KEY_V},
+        {AKEYCODE_W, KEY_W},
+        {AKEYCODE_X, KEY_X},
+        {AKEYCODE_Y, KEY_Y},
+        {AKEYCODE_Z, KEY_Z},
+        {AKEYCODE_GRAVE, KEY_GRAVE},
+        {AKEYCODE_MINUS, KEY_MINUS},
+        {AKEYCODE_EQUALS, KEY_EQUAL},
+        {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
+        {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
+        {AKEYCODE_BACKSLASH, KEY_BACKSLASH},
+        {AKEYCODE_SEMICOLON, KEY_SEMICOLON},
+        {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
+        {AKEYCODE_COMMA, KEY_COMMA},
+        {AKEYCODE_PERIOD, KEY_DOT},
+        {AKEYCODE_SLASH, KEY_SLASH},
+        {AKEYCODE_ALT_LEFT, KEY_LEFTALT},
+        {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
+        {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
+        {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
+        {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
+        {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
+        {AKEYCODE_META_LEFT, KEY_LEFTMETA},
+        {AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
+        {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
+        {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
+        {AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
+        {AKEYCODE_ENTER, KEY_ENTER},
+        {AKEYCODE_TAB, KEY_TAB},
+        {AKEYCODE_SPACE, KEY_SPACE},
+        {AKEYCODE_DPAD_DOWN, KEY_DOWN},
+        {AKEYCODE_DPAD_UP, KEY_UP},
+        {AKEYCODE_DPAD_LEFT, KEY_LEFT},
+        {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
+        {AKEYCODE_MOVE_END, KEY_END},
+        {AKEYCODE_MOVE_HOME, KEY_HOME},
+        {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
+        {AKEYCODE_PAGE_UP, KEY_PAGEUP},
+        {AKEYCODE_DEL, KEY_BACKSPACE},
+        {AKEYCODE_FORWARD_DEL, KEY_DELETE},
+        {AKEYCODE_INSERT, KEY_INSERT},
+        {AKEYCODE_ESCAPE, KEY_ESC},
+        {AKEYCODE_BREAK, KEY_PAUSE},
+        {AKEYCODE_F1, KEY_F1},
+        {AKEYCODE_F2, KEY_F2},
+        {AKEYCODE_F3, KEY_F3},
+        {AKEYCODE_F4, KEY_F4},
+        {AKEYCODE_F5, KEY_F5},
+        {AKEYCODE_F6, KEY_F6},
+        {AKEYCODE_F7, KEY_F7},
+        {AKEYCODE_F8, KEY_F8},
+        {AKEYCODE_F9, KEY_F9},
+        {AKEYCODE_F10, KEY_F10},
+        {AKEYCODE_F11, KEY_F11},
+        {AKEYCODE_F12, KEY_F12},
+        {AKEYCODE_BACK, KEY_BACK},
+        {AKEYCODE_FORWARD, KEY_FORWARD},
+        {AKEYCODE_NUMPAD_1, KEY_KP1},
+        {AKEYCODE_NUMPAD_2, KEY_KP2},
+        {AKEYCODE_NUMPAD_3, KEY_KP3},
+        {AKEYCODE_NUMPAD_4, KEY_KP4},
+        {AKEYCODE_NUMPAD_5, KEY_KP5},
+        {AKEYCODE_NUMPAD_6, KEY_KP6},
+        {AKEYCODE_NUMPAD_7, KEY_KP7},
+        {AKEYCODE_NUMPAD_8, KEY_KP8},
+        {AKEYCODE_NUMPAD_9, KEY_KP9},
+        {AKEYCODE_NUMPAD_0, KEY_KP0},
+        {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
+        {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
+        {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
+        {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
+        {AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
+        {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
+        {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
+        {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
+};
+
+/** Creates a new uinput device and assigns a file descriptor. */
+static int openUinput(const char* readableName, jint vendorId, jint productId,
+                      DeviceType deviceType, jint screenHeight, jint screenWidth) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+    if (fd < 0) {
+        ALOGE("Error creating uinput device: %s", strerror(errno));
+        return -errno;
+    }
+
+    ioctl(fd, UI_SET_EVBIT, EV_KEY);
+    ioctl(fd, UI_SET_EVBIT, EV_SYN);
+    switch (deviceType) {
+        case DeviceType::KEYBOARD:
+            for (const auto& [ignored, keyCode] : KEY_CODE_MAPPING) {
+                ioctl(fd, UI_SET_KEYBIT, keyCode);
+            }
+            break;
+        case DeviceType::MOUSE:
+            ioctl(fd, UI_SET_EVBIT, EV_REL);
+            ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
+            ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
+            ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
+            ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
+            ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
+            ioctl(fd, UI_SET_RELBIT, REL_X);
+            ioctl(fd, UI_SET_RELBIT, REL_Y);
+            ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+            ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
+            break;
+        case DeviceType::TOUCHSCREEN:
+            ioctl(fd, UI_SET_EVBIT, EV_ABS);
+            ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+            ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
+            ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    }
+
+    int version;
+    if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
+        uinput_setup setup;
+        memset(&setup, 0, sizeof(setup));
+        strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
+        setup.id.version = 1;
+        setup.id.bustype = BUS_VIRTUAL;
+        setup.id.vendor = vendorId;
+        setup.id.product = productId;
+        if (deviceType == DeviceType::TOUCHSCREEN) {
+            uinput_abs_setup xAbsSetup;
+            xAbsSetup.code = ABS_MT_POSITION_X;
+            xAbsSetup.absinfo.maximum = screenWidth - 1;
+            xAbsSetup.absinfo.minimum = 0;
+            ioctl(fd, UI_ABS_SETUP, xAbsSetup);
+            uinput_abs_setup yAbsSetup;
+            yAbsSetup.code = ABS_MT_POSITION_Y;
+            yAbsSetup.absinfo.maximum = screenHeight - 1;
+            yAbsSetup.absinfo.minimum = 0;
+            ioctl(fd, UI_ABS_SETUP, yAbsSetup);
+            uinput_abs_setup majorAbsSetup;
+            majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
+            majorAbsSetup.absinfo.maximum = screenWidth - 1;
+            majorAbsSetup.absinfo.minimum = 0;
+            ioctl(fd, UI_ABS_SETUP, majorAbsSetup);
+            uinput_abs_setup pressureAbsSetup;
+            pressureAbsSetup.code = ABS_MT_PRESSURE;
+            pressureAbsSetup.absinfo.maximum = 255;
+            pressureAbsSetup.absinfo.minimum = 0;
+            ioctl(fd, UI_ABS_SETUP, pressureAbsSetup);
+        }
+        if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
+            ALOGE("Error creating uinput device: %s", strerror(errno));
+            return -errno;
+        }
+    } else {
+        // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
+        uinput_user_dev fallback;
+        memset(&fallback, 0, sizeof(fallback));
+        strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
+        fallback.id.version = 1;
+        fallback.id.bustype = BUS_VIRTUAL;
+        fallback.id.vendor = vendorId;
+        fallback.id.product = productId;
+        if (deviceType == DeviceType::TOUCHSCREEN) {
+            fallback.absmin[ABS_MT_POSITION_X] = 0;
+            fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
+            fallback.absmin[ABS_MT_POSITION_Y] = 0;
+            fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
+            fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
+            fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
+            fallback.absmin[ABS_MT_PRESSURE] = 0;
+            fallback.absmax[ABS_MT_PRESSURE] = 255;
+        }
+        if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
+            ALOGE("Error creating uinput device: %s", strerror(errno));
+            return -errno;
+        }
+    }
+
+    if (ioctl(fd, UI_DEV_CREATE) != 0) {
+        ALOGE("Error creating uinput device: %s", strerror(errno));
+        return -errno;
+    }
+
+    return fd.release();
+}
+
+static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
+                         DeviceType deviceType, int screenHeight, int screenWidth) {
+    ScopedUtfChars readableName(env, name);
+    return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight,
+                      screenWidth);
+}
+
+static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                    jint productId) {
+    return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0,
+                         /* screenWidth */ 0);
+}
+
+static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                 jint productId) {
+    return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0,
+                         /* screenWidth */ 0);
+}
+
+static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                       jint productId, jint height, jint width) {
+    return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width);
+}
+
+static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
+    ioctl(fd, UI_DEV_DESTROY);
+    return close(fd);
+}
+
+static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value) {
+    struct input_event ev = {.type = type, .code = code, .value = value};
+    return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev);
+}
+
+static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+                                jint action) {
+    auto keyCodeIterator = KEY_CODE_MAPPING.find(androidKeyCode);
+    if (keyCodeIterator == KEY_CODE_MAPPING.end()) {
+        ALOGE("No supportive native keycode for androidKeyCode %d", androidKeyCode);
+        return false;
+    }
+    auto actionIterator = KEY_ACTION_MAPPING.find(action);
+    if (actionIterator == KEY_ACTION_MAPPING.end()) {
+        return false;
+    }
+    if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(keyCodeIterator->second),
+                         static_cast<int32_t>(actionIterator->second))) {
+        return false;
+    }
+    if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
+        return false;
+    }
+    return true;
+}
+
+static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode,
+                                   jint action) {
+    auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode);
+    if (buttonCodeIterator == BUTTON_CODE_MAPPING.end()) {
+        return false;
+    }
+    auto actionIterator = BUTTON_ACTION_MAPPING.find(action);
+    if (actionIterator == BUTTON_ACTION_MAPPING.end()) {
+        return false;
+    }
+    if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(buttonCodeIterator->second),
+                         static_cast<int32_t>(actionIterator->second))) {
+        return false;
+    }
+    if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
+        return false;
+    }
+    return true;
+}
+
+static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint pointerId, jint toolType,
+                                  jint action, jfloat locationX, jfloat locationY, jfloat pressure,
+                                  jfloat majorAxisSize) {
+    if (!writeInputEvent(fd, EV_ABS, ABS_MT_SLOT, pointerId)) {
+        return false;
+    }
+    auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
+    if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
+        return false;
+    }
+    if (toolType != -1) {
+        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOOL_TYPE,
+                             static_cast<int32_t>(toolTypeIterator->second))) {
+            return false;
+        }
+    }
+    auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
+    if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
+        return false;
+    }
+    UinputAction uinputAction = actionIterator->second;
+    if (uinputAction == UinputAction::PRESS || uinputAction == UinputAction::RELEASE) {
+        if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(uinputAction))) {
+            return false;
+        }
+        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID,
+                             static_cast<int32_t>(uinputAction == UinputAction::PRESS ? pointerId
+                                                                                      : -1))) {
+            return false;
+        }
+    }
+    if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_X, locationX)) {
+        return false;
+    }
+    if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_Y, locationY)) {
+        return false;
+    }
+    if (!isnan(pressure)) {
+        if (!writeInputEvent(fd, EV_ABS, ABS_MT_PRESSURE, pressure)) {
+            return false;
+        }
+    }
+    if (!isnan(majorAxisSize)) {
+        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
+            return false;
+        }
+    }
+    return writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jint fd, jfloat relativeX,
+                                     jfloat relativeY) {
+    return writeInputEvent(fd, EV_REL, REL_X, relativeX) &&
+            writeInputEvent(fd, EV_REL, REL_Y, relativeY) &&
+            writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xAxisMovement,
+                                   jfloat yAxisMovement) {
+    return writeInputEvent(fd, EV_REL, REL_HWHEEL, xAxisMovement) &&
+            writeInputEvent(fd, EV_REL, REL_WHEEL, yAxisMovement) &&
+            writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static JNINativeMethod methods[] = {
+        {"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard},
+        {"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse},
+        {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I",
+         (void*)nativeOpenUinputTouchscreen},
+        {"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
+        {"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
+        {"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent},
+        {"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent},
+        {"nativeWriteRelativeEvent", "(IFF)Z", (void*)nativeWriteRelativeEvent},
+        {"nativeWriteScrollEvent", "(IFF)Z", (void*)nativeWriteScrollEvent},
+};
+
+int register_android_server_companion_virtual_InputController(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/server/companion/virtual/InputController",
+                                    methods, NELEM(methods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ff61abc..d339ef1 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -63,6 +63,7 @@
 int register_android_server_GpuService(JNIEnv* env);
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
 int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
+int register_android_server_companion_virtual_InputController(JNIEnv* env);
 };
 
 using namespace android;
@@ -119,5 +120,6 @@
     register_android_server_GpuService(env);
     register_android_server_stats_pull_StatsPullAtomService(env);
     register_android_server_sensor_SensorService(vm, env);
+    register_android_server_companion_virtual_InputController(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 4a35fdf..d7e3195 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -24,17 +24,7 @@
 import android.content.pm.PackageManager
 import android.content.pm.SigningDetails
 import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedAttributionImpl
-import android.content.pm.parsing.component.ParsedComponentImpl
-import android.content.pm.parsing.component.ParsedInstrumentationImpl
-import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.parsing.component.ParsedPermissionGroupImpl
-import android.content.pm.parsing.component.ParsedPermissionImpl
-import android.content.pm.parsing.component.ParsedProcessImpl
-import android.content.pm.parsing.component.ParsedProviderImpl
-import android.content.pm.parsing.component.ParsedServiceImpl
-import android.content.pm.parsing.component.ParsedUsesPermissionImpl
+import android.content.pm.parsing.component.*
 import android.net.Uri
 import android.os.Bundle
 import android.os.Parcelable
@@ -360,6 +350,13 @@
             transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
+            AndroidPackage::getApexSystemServices,
+            PackageImpl::addApexSystemService,
+            "TestApexSystemServiceName",
+            transformGet = { it.singleOrNull()?.name.orEmpty() },
+            transformSet = { ParsedApexSystemServiceImpl().apply { name = it } }
+        ),
+        getSetByValue(
             AndroidPackage::getReceivers,
             PackageImpl::addReceiver,
             "TestReceiverName",
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e3c60fd..c3a364e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -32,6 +32,7 @@
         "services.appwidget",
         "services.autofill",
         "services.backup",
+        "services.companion",
         "services.core",
         "services.devicepolicy",
         "services.net",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 8b6b7c2..1d6ed03 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -296,14 +296,6 @@
     }
 
     @Test
-    public void testToggleSplitScreen_legacy() {
-        setupWithRealContext();
-        mSystemActionPerformer.performSystemAction(
-                AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
-        verify(mMockStatusBarManagerInternal).toggleSplitScreen();
-    }
-
-    @Test
     public void testScreenshot_requestsFromScreenshotHelper_legacy() {
         setupWithMockContext();
         mSystemActionPerformer.performSystemAction(
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
new file mode 100644
index 0000000..c7c0756
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -0,0 +1,313 @@
+/*
+ * 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.companion.virtual;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.Point;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.VirtualKeyEvent;
+import android.hardware.input.VirtualMouseButtonEvent;
+import android.hardware.input.VirtualMouseRelativeEvent;
+import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualTouchEvent;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.KeyEvent;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class VirtualDeviceManagerServiceTest {
+
+    private static final String DEVICE_NAME = "device name";
+    private static final int DISPLAY_ID = 2;
+    private static final int PRODUCT_ID = 10;
+    private static final int VENDOR_ID = 5;
+    private static final int HEIGHT = 1800;
+    private static final int WIDTH = 900;
+    private static final Binder BINDER = new Binder("binder");
+
+    private Context mContext;
+    private VirtualDeviceImpl mDeviceImpl;
+    private InputController mInputController;
+    @Mock
+    private InputController.NativeWrapper mNativeWrapperMock;
+    @Mock
+    private DisplayManagerInternal mDisplayManagerInternalMock;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+        mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+        doNothing().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+        mInputController = new InputController(new Object(), mNativeWrapperMock);
+        mDeviceImpl = new VirtualDeviceImpl(mContext,
+                /* association info */ null, new Binder(), /* uid */ 0, mInputController,
+                (int associationId) -> {});
+    }
+
+    @Test
+    public void createVirtualKeyboard_noDisplay_failsSecurityException() {
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+                        PRODUCT_ID, BINDER));
+    }
+
+    @Test
+    public void createVirtualMouse_noDisplay_failsSecurityException() {
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+                        PRODUCT_ID, BINDER));
+    }
+
+    @Test
+    public void createVirtualTouchscreen_noDisplay_failsSecurityException() {
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME,
+                        VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT)));
+    }
+
+    @Test
+    public void createVirtualKeyboard_noPermission_failsSecurityException() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+                        PRODUCT_ID, BINDER));
+    }
+
+    @Test
+    public void createVirtualMouse_noPermission_failsSecurityException() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+                        PRODUCT_ID, BINDER));
+    }
+
+    @Test
+    public void createVirtualTouchscreen_noPermission_failsSecurityException() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+        assertThrows(
+                SecurityException.class,
+                () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME,
+                        VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT)));
+    }
+
+    @Test
+    public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+                BINDER);
+        assertWithMessage("Virtual keyboard should register fd when the display matches")
+                .that(mInputController.mInputDeviceFds).isNotEmpty();
+        verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+    }
+
+    @Test
+    public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+                BINDER);
+        assertWithMessage("Virtual keyboard should register fd when the display matches")
+                .that(mInputController.mInputDeviceFds).isNotEmpty();
+        verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
+    }
+
+    @Test
+    public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+                BINDER, new Point(WIDTH, HEIGHT));
+        assertWithMessage("Virtual keyboard should register fd when the display matches")
+                .that(mInputController.mInputDeviceFds).isNotEmpty();
+        verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
+                WIDTH);
+    }
+
+    @Test
+    public void sendKeyEvent_noFd() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
+                                .setKeyCode(KeyEvent.KEYCODE_A)
+                                .setAction(VirtualKeyEvent.ACTION_DOWN).build()));
+    }
+
+    @Test
+    public void sendKeyEvent_hasFd_writesEvent() {
+        final int fd = 1;
+        final int keyCode = KeyEvent.KEYCODE_A;
+        final int action = VirtualKeyEvent.ACTION_UP;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
+                .setAction(action).build());
+        verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
+    }
+
+    @Test
+    public void sendButtonEvent_noFd() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        mDeviceImpl.sendButtonEvent(BINDER,
+                                new VirtualMouseButtonEvent.Builder()
+                                        .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK)
+                                        .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+                                        .build()));
+    }
+
+    @Test
+    public void sendButtonEvent_hasFd_writesEvent() {
+        final int fd = 1;
+        final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
+        final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
+                .setButtonCode(buttonCode)
+                .setAction(action).build());
+        verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action);
+    }
+
+    @Test
+    public void sendRelativeEvent_noFd() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        mDeviceImpl.sendRelativeEvent(BINDER,
+                                new VirtualMouseRelativeEvent.Builder().setRelativeX(
+                                        0.0f).setRelativeY(0.0f).build()));
+    }
+
+    @Test
+    public void sendRelativeEvent_hasFd_writesEvent() {
+        final int fd = 1;
+        final float x = -0.2f;
+        final float y = 0.7f;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
+                .setRelativeX(x).setRelativeY(y).build());
+        verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
+    }
+
+    @Test
+    public void sendScrollEvent_noFd() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        mDeviceImpl.sendScrollEvent(BINDER,
+                                new VirtualMouseScrollEvent.Builder()
+                                        .setXAxisMovement(-1f)
+                                        .setYAxisMovement(1f).build()));
+    }
+
+    @Test
+    public void sendScrollEvent_hasFd_writesEvent() {
+        final int fd = 1;
+        final float x = 0.5f;
+        final float y = 1f;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
+                .setXAxisMovement(x)
+                .setYAxisMovement(y).build());
+        verify(mNativeWrapperMock).writeScrollEvent(fd, x, y);
+    }
+
+    @Test
+    public void sendTouchEvent_noFd() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
+                                .setX(0.0f)
+                                .setY(0.0f)
+                                .setAction(VirtualTouchEvent.ACTION_UP)
+                                .setPointerId(1)
+                                .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+                                .build()));
+    }
+
+    @Test
+    public void sendTouchEvent_hasFd_writesEvent_withoutPressureOrMajorAxisSize() {
+        final int fd = 1;
+        final int pointerId = 5;
+        final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER;
+        final float x = 100.5f;
+        final float y = 200.5f;
+        final int action = VirtualTouchEvent.ACTION_UP;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
+                .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
+        verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
+                Float.NaN);
+    }
+
+    @Test
+    public void sendTouchEvent_hasFd_writesEvent() {
+        final int fd = 1;
+        final int pointerId = 5;
+        final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER;
+        final float x = 100.5f;
+        final float y = 200.5f;
+        final int action = VirtualTouchEvent.ACTION_UP;
+        final float pressure = 1.0f;
+        final float majorAxisSize = 10.0f;
+        mInputController.mInputDeviceFds.put(BINDER, fd);
+        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
+                .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
+                .setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
+        verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
+                majorAxisSize);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3722ba4..58c9db7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -579,7 +579,7 @@
         }
 
         @Override
-        public void verifyCallingPackage(String callingPackage) {
+        public void verifyCallingPackage(String callingPackage, int callerUid) {
             // SKIP
         }
 
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 11bac45..c888524 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -46,6 +46,7 @@
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityImpl;
+import android.content.pm.parsing.component.ParsedApexSystemService;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedInstrumentationImpl;
@@ -526,6 +527,23 @@
     }
 
     @Test
+    public void testParseApexSystemService() throws Exception {
+        final File testFile = extractFile(TEST_APP4_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedApexSystemService> systemServices = pkg.getApexSystemServices();
+            for (ParsedApexSystemService systemService: systemServices) {
+                assertEquals(PACKAGE_NAME + ".SystemService", systemService.getName());
+                assertEquals("service-test.jar", systemService.getJarPath());
+                assertEquals("30", systemService.getMinSdkVersion());
+                assertEquals("31", systemService.getMaxSdkVersion());
+            }
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    @Test
     public void testParseModernPackageHasNoCompatPermissions() throws Exception {
         final File testFile = extractFile(TEST_APP1_APK);
         try {
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
index 299b9a0..70fd28d 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -57,6 +57,11 @@
 	        <property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" />
 	        <property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" />
 	    </service>
+		<apex-system-service
+			android:name="com.android.servicestests.apps.packageparserapp.SystemService"
+			android:path="service-test.jar"
+			android:minSdkVersion = "30"
+			android:maxSdkVersion = "31" />
     </application>
 
     <instrumentation
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ae22247..cca5e2f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8551,12 +8551,58 @@
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
             TelephonyScanManager.NetworkScanCallback callback) {
-        synchronized (this) {
+        return requestNetworkScan(false, request, executor, callback);
+    }
+
+    /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+     *
+     * If the system-wide location switch is off, apps may still call this API, with the
+     * following constraints:
+     * <ol>
+     *     <li>The app must hold the {@code android.permission.NETWORK_SCAN} permission.</li>
+     *     <li>The app must not supply any specific bands or channels to scan.</li>
+     *     <li>The app must only specify MCC/MNC pairs that are
+     *     associated to a SIM in the device.</li>
+     *     <li>Returned results will have no meaningful info other than signal strength
+     *     and MCC/MNC info.</li>
+     * </ol>
+     *
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
+     * @param callback Returns network scan results or errors.
+     * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public @Nullable NetworkScan requestNetworkScan(
+            boolean renounceFineLocationAccess, @NonNull NetworkScanRequest request,
+            @NonNull Executor executor,
+            @NonNull TelephonyScanManager.NetworkScanCallback callback) {
+        synchronized (sCacheLock) {
             if (mTelephonyScanManager == null) {
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), renounceFineLocationAccess,
+                request, executor, callback,
                 getOpPackageName(), getAttributionTag());
     }
 
@@ -11613,7 +11659,6 @@
      * <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}.
-     *
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
      */
@@ -11623,7 +11668,72 @@
             Manifest.permission.ACCESS_COARSE_LOCATION
     })
     public @Nullable ServiceState getServiceState() {
-        return getServiceStateForSubscriber(getSubId());
+        return getServiceState(false, false);
+    }
+
+    /**
+     * Returns the current {@link ServiceState} information.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * If you want continuous updates of service state info, register a {@link PhoneStateListener}
+     * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event.
+     *
+     * <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}.
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+     * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+     * receive location related information which will be sent if the caller already possess
+     * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+     * 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
+    })
+    public @Nullable ServiceState getServiceState(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess) {
+        return getServiceStateForSubscriber(getSubId(), renounceFineLocationAccess,
+                renounceCoarseLocationAccess);
+    }
+
+    /**
+     * Returns the service state information on specified subscription. Callers require
+     * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+     * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+     * receive location related information which will be sent if the caller already possess
+     * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+     */
+    private ServiceState getServiceStateForSubscriber(int subId,
+            boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess,
+                        renounceCoarseLocationAccess,
+                        getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+        } catch (NullPointerException e) {
+            AnomalyReporter.reportAnomaly(
+                    UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
+                    "getServiceStateForSubscriber " + subId + " NPE");
+        }
+        return null;
     }
 
     /**
@@ -11636,20 +11746,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public ServiceState getServiceStateForSubscriber(int subId) {
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                return service.getServiceStateForSubscriber(subId, getOpPackageName(),
-                        getAttributionTag());
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
-        } catch (NullPointerException e) {
-            AnomalyReporter.reportAnomaly(
-                    UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
-                    "getServiceStateForSubscriber " + subId + " NPE");
-        }
-        return null;
+        return getServiceStateForSubscriber(getSubId(), false, false);
     }
 
     /**
@@ -15500,7 +15597,49 @@
      */
     public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull TelephonyCallback callback) {
+        registerTelephonyCallback(false, false, executor, callback);
+    }
 
+    /**
+     * Registers a callback object to receive notification of changes in specified telephony states.
+     * <p>
+     * To register a callback, pass a {@link TelephonyCallback} which implements
+     * interfaces of events. For example,
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the callback object and passes the current (updated)
+     * values.
+     * <p>
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+     * subIds, pass a separate callback object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
+     *
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permissions.
+     * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+     * receive location related information which will be sent if the caller already possess
+     * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+     * @param executor The executor of where the callback will execute.
+     * @param callback The {@link TelephonyCallback} object to register.
+     */
+    public void registerTelephonyCallback(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull TelephonyCallback callback) {
         if (mContext == null) {
             throw new IllegalStateException("telephony service is null.");
         }
@@ -15511,7 +15650,8 @@
         mTelephonyRegistryMgr = (TelephonyRegistryManager)
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (mTelephonyRegistryMgr != null) {
-            mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(),
+            mTelephonyRegistryMgr.registerTelephonyCallback(renounceFineLocationAccess,
+                    renounceCoarseLocationAccess, executor, mSubId, getOpPackageName(),
                     getAttributionTag(), callback, getITelephony() != null);
         } else {
             throw new IllegalStateException("telephony service is null.");
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 9572154..122662d 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -228,7 +228,9 @@
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
-     *
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission.ACCESS_FINE_LOCATION} and do not renounce the permission
      * @param request Contains all the RAT with bands/channels that need to be scanned.
      * @param callback Returns network scan results or errors.
      * @param callingPackage The package name of the caller
@@ -237,6 +239,7 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
+            boolean renounceFineLocationAccess,
             NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
             String callingPackage, @Nullable String callingFeatureId) {
         try {
@@ -252,7 +255,8 @@
             // the record to the ScanInfo cache.
             synchronized (mScanInfo) {
                 int scanId = telephony.requestNetworkScan(
-                        subId, request, mMessenger, new Binder(), callingPackage,
+                        subId, renounceFineLocationAccess, request, mMessenger,
+                        new Binder(), callingPackage,
                         callingFeatureId);
                 if (scanId == INVALID_SCAN_ID) {
                     Rlog.e(TAG, "Failed to initiate network scan");
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index 793c377..c25ace0 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -50,6 +50,7 @@
     private String mRcsProfile;
     private String mClientVendor;
     private String mClientVersion;
+    private boolean mRcsEnabledByUser;
 
     /**
      * Create a RcsClientConfiguration object.
@@ -63,14 +64,41 @@
      * @param clientVersion Identifies the RCS client version. Refer to GSMA
      * RCC.07 "client_version" parameter.
      * Example:client_version=RCSAndrd-1.0
+     * @deprecated Use {@link #RcsClientConfiguration(String, String, String, String, boolean)}
+     * instead. Deprecated prototype assumes that the user setting controlling RCS is enabled.
      */
+    @Deprecated
     public RcsClientConfiguration(@NonNull String rcsVersion,
             @NonNull @StringRcsProfile String rcsProfile,
             @NonNull String clientVendor, @NonNull String clientVersion) {
+        this(rcsVersion, rcsProfile, clientVendor, clientVersion, true);
+    }
+
+    /**
+     * Create a RcsClientConfiguration object.
+     * Default messaging application must pass a valid configuration object
+     * @param rcsVersion The parameter identifies the RCS version supported
+     * by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
+     * @param rcsProfile Identifies a fixed set of RCS services that are
+     * supported by the client. See {@link #RCS_PROFILE_1_0 } or
+     * {@link #RCS_PROFILE_2_3 }
+     * @param clientVendor Identifies the vendor providing the RCS client.
+     * @param clientVersion Identifies the RCS client version. Refer to GSMA
+     * RCC.07 "client_version" parameter.
+     * Example:client_version=RCSAndrd-1.0
+     * @param isRcsEnabledByUser The current user setting for whether or not the user has
+     * enabled or disabled RCS. Please refer to GSMA RCC.07 "rcs_state" parameter for how this
+     * can affect provisioning.
+     */
+    public RcsClientConfiguration(@NonNull String rcsVersion,
+            @NonNull @StringRcsProfile String rcsProfile,
+            @NonNull String clientVendor, @NonNull String clientVersion,
+            boolean isRcsEnabledByUser) {
         mRcsVersion = rcsVersion;
         mRcsProfile = rcsProfile;
         mClientVendor = clientVendor;
         mClientVersion = clientVersion;
+        mRcsEnabledByUser = isRcsEnabledByUser;
     }
 
     /**
@@ -102,6 +130,18 @@
     }
 
     /**
+     * The current user setting provided by the RCS messaging application that determines
+     * whether or not the user has enabled RCS.
+     * <p>
+     * See GSMA RCC.07 "rcs_state" parameter for more information about how this setting
+     * affects provisioning.
+     * @return true if RCS is enabled by the user, false if RCS is disabled by the user.
+     */
+    public boolean isRcsEnabledByUser() {
+        return mRcsEnabledByUser;
+    }
+
+    /**
      * {@link Parcelable#writeToParcel}
      */
     @Override
@@ -110,6 +150,7 @@
         out.writeString(mRcsProfile);
         out.writeString(mClientVendor);
         out.writeString(mClientVersion);
+        out.writeBoolean(mRcsEnabledByUser);
     }
 
     /**
@@ -124,8 +165,9 @@
                     String rcsProfile = in.readString();
                     String clientVendor = in.readString();
                     String clientVersion = in.readString();
+                    Boolean rcsEnabledByUser = in.readBoolean();
                     return new RcsClientConfiguration(rcsVersion, rcsProfile,
-                            clientVendor, clientVersion);
+                            clientVendor, clientVersion, rcsEnabledByUser);
                 }
 
                 @Override
@@ -152,11 +194,13 @@
 
         return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile)
                 && mClientVendor.equals(other.mClientVendor)
-                && mClientVersion.equals(other.mClientVersion);
+                && mClientVersion.equals(other.mClientVersion)
+                && (mRcsEnabledByUser == other.mRcsEnabledByUser);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion);
+        return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion,
+                mRcsEnabledByUser);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 7a1c092..61de3ac 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -28,13 +28,10 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -337,6 +334,14 @@
     @SystemApi
     public static final int PUBLISH_STATE_OTHER_ERROR = 6;
 
+    /**
+     * The device is currently trying to publish its capabilities to the network.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_PUBLISHING = 7;
+
+
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "PUBLISH_STATE_", value = {
@@ -345,7 +350,8 @@
             PUBLISH_STATE_VOICE_PROVISION_ERROR,
             PUBLISH_STATE_RCS_PROVISION_ERROR,
             PUBLISH_STATE_REQUEST_TIMEOUT,
-            PUBLISH_STATE_OTHER_ERROR
+            PUBLISH_STATE_OTHER_ERROR,
+            PUBLISH_STATE_PUBLISHING
     })
     public @interface PublishState {}
 
@@ -480,9 +486,12 @@
      * <p>
      * Be sure to check the availability of this feature using
      * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
-     * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+     * enabled or else this operation will fail with {@link #ERROR_NOT_AVAILABLE} or
+     * {@link #ERROR_NOT_ENABLED}.
      *
      * @param contactNumbers A list of numbers that the capabilities are being requested for.
      * @param executor The executor that will be used when the request is completed and the
@@ -573,8 +582,10 @@
      * <p>
      * Be sure to check the availability of this feature using
      * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
      * enabled or else this operation will fail with
      * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
      *
@@ -690,7 +701,8 @@
      * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
      * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
      * <p>
-     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
+     * Use {@link android.telephony.SubscriptionManager.OnSubscriptionsChangedListener} to listen
+     * to subscription
      * changed events and call
      * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
      * <p>
@@ -792,7 +804,8 @@
      * cache associated with those contacts as the local cache becomes stale.
      * <p>
      * This setting will only enable this feature if
-     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
+     * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+     * also enabled.
      * <p>
      * Note: This setting does not affect whether or not the device publishes its service
      * capabilities if the subscription supports presence publication.
@@ -843,7 +856,8 @@
      * session.
      * <p>
      * This setting will only enable this feature if
-     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
+     * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+     * also enabled.
      * <p>
      * Note: This setting does not affect whether or not the device publishes its service
      * capabilities if the subscription supports presence publication.
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index c3d7325..c27fa4f 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -81,6 +81,26 @@
     }
 
     /**
+     * Receives the status of changes in the publishing connection from ims service
+     * and deliver this callback to the framework.
+     */
+    public void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+            int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onPublishUpdated(reasonCode, reasonPhrase,
+                    reasonHeaderCause, reasonHeaderText);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
      * Receives the callback of the remote capability request from the network and deliver this
      * request to the framework.
      */
diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
index 078ac91..c675bc3 100644
--- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
@@ -31,6 +31,8 @@
 oneway interface ICapabilityExchangeEventListener {
     void onRequestPublishCapabilities(int publishTriggerType);
     void onUnpublish();
+    void onPublishUpdated(int reasonCode, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText);
     void onRemoteCapabilityRequest(in Uri contactUri,
             in List<String> remoteCapabilities, IOptionsRequestCallback cb);
 }
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index a3be8da..9293a40 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -90,6 +90,30 @@
     void onUnpublish() throws ImsException;
 
     /**
+     * Notify the framework that the ImsService has refreshed the PUBLISH
+     * internally, which has resulted in a new PUBLISH result.
+     * <p>
+     * This method must return both SUCCESS (200 OK) and FAILURE (300+) codes in order to
+     * keep the AOSP stack up to date.
+     * @param reasonCode The SIP response code sent from the network.
+     * @param reasonPhrase The optional reason response from the network. If the
+     * network provided no reason with the sip code, the string should be empty.
+     * @param reasonHeaderCause The “cause” parameter of the “reason” header
+     * included in the SIP message.
+     * @param reasonHeaderText The “text” parameter of the “reason” header
+     * included in the SIP message.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+     * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+     * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+     * cases when the Telephony stack has crashed.
+     *
+     */
+    default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+            int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+    }
+
+    /**
      * Inform the framework of an OPTIONS query from a remote device for this device's UCE
      * capabilities.
      * <p>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1f336f7..f317c92 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -871,6 +871,8 @@
      * Perform a radio network scan and return the id of this scan.
      *
      * @param subId the id of the subscription.
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to
+     * receive fine location related information
      * @param request Defines all the configs for network scan.
      * @param messenger Callback messages will be sent using this messenger.
      * @param binder the binder object instantiated in TelephonyManager.
@@ -878,8 +880,9 @@
      * @param callingFeatureId The feature in the package
      * @return An id for this scan.
      */
-    int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
-            in IBinder binder, in String callingPackage, String callingFeatureId);
+    int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
+            in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
+	    in String callingPackage, String callingFeatureId);
 
     /**
      * Stop an existing radio network scan.
@@ -1353,12 +1356,17 @@
     /**
      * Get the service state on specified subscription
      * @param subId Subscription id
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to
+     * receive fine location related information
+     * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+     * receive coarse location related information
      * @param callingPackage The package making the call
      * @param callingFeatureId The feature in the package
      * @return Service state on specified subscription.
      */
-    ServiceState getServiceStateForSubscriber(int subId, String callingPackage,
-            String callingFeatureId);
+    ServiceState getServiceStateForSubscriber(int subId, boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess,
+            String callingPackage, String callingFeatureId);
 
     /**
      * Returns the URI for the per-account voicemail ringtone set in Phone settings.
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index d1a68d4..c5169e5 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -235,6 +235,7 @@
     method @Deprecated public android.content.Intent getLaunchIntentForPackage(String);
     method @Deprecated public android.content.Intent getLeanbackLaunchIntentForPackage(String);
     method @Deprecated public String getNameForUid(int);
+    method @Deprecated public android.content.pm.PackageInfo getPackageArchiveInfo(String, int);
     method @Deprecated public int[] getPackageGids(String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 5d0d718..3914ef6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -231,7 +231,7 @@
     @Test
     fun screenLockedStart() {
         testSpec.assertLayersStart {
-            isVisible(colorFadComponent)
+            isEmpty()
         }
     }
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index b46a125..d204190 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -544,6 +544,7 @@
   application_action["activity-alias"] = component_action;
   application_action["service"] = component_action;
   application_action["receiver"] = component_action;
+  application_action["apex-system-service"] = component_action;
 
   // Provider actions.
   application_action["provider"] = component_action;
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 98c0e69..3e12971 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -12,10 +12,10 @@
     manifest: "manifest.txt",
     srcs: ["src/**/*.java"],
     static_libs: [
-        "asm-6.0",
-        "asm-commons-6.0",
-        "asm-tree-6.0",
-        "asm-analysis-6.0",
+        "asm-7.0",
+        "asm-commons-7.0",
+        "asm-tree-7.0",
+        "asm-analysis-7.0",
         "guava-21.0",
     ],
 }
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
index 1002c88..335b3f8 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockTargetStateAnalysis.java
@@ -13,8 +13,6 @@
  */
 package lockedregioncodeinjection;
 
-import java.util.ArrayList;
-import java.util.List;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.tree.AbstractInsnNode;
 import org.objectweb.asm.tree.MethodInsnNode;
@@ -22,6 +20,9 @@
 import org.objectweb.asm.tree.analysis.BasicInterpreter;
 import org.objectweb.asm.tree.analysis.BasicValue;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A simple dataflow analysis to determine if the operands on the stack must be one of target lock
  * class type.
@@ -31,6 +32,7 @@
     private final List<LockTarget> targetLocks;
 
     public LockTargetStateAnalysis(List<LockTarget> targetLocks) {
+        super(Utils.ASM_VERSION);
         this.targetLocks = targetLocks;
     }
 
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
index 219c2b3..f1e84b1 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -13,13 +13,14 @@
  */
 package lockedregioncodeinjection;
 
+import org.objectweb.asm.Opcodes;
+
 import java.util.ArrayList;
 import java.util.List;
-import org.objectweb.asm.Opcodes;
 
 public class Utils {
 
-    public static final int ASM_VERSION = Opcodes.ASM6;
+    public static final int ASM_VERSION = Opcodes.ASM7;
 
     /**
      * Reads a comma separated configuration similar to the Jack definition.
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
index c408b9e..31fa0bf 100644
--- a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
@@ -30,7 +30,7 @@
  * rm -fr out/*
  *
  * # Make booster
- * javac -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar src&#47;*&#47;*.java -d out/
+ * javac -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar src&#47;*&#47;*.java -d out/
  * pushd out
  * jar cfe lockedregioncodeinjection.jar lockedregioncodeinjection.Main *&#47;*.class
  * popd
@@ -43,7 +43,7 @@
  * popd
  *
  * # Run tool on unit tests.
- * java -ea -cp lib/asm-6.0_BETA.jar:lib/asm-commons-6.0_BETA.jar:lib/asm-tree-6.0_BETA.jar:lib/asm-analysis-6.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
+ * java -ea -cp lib/asm-7.0_BETA.jar:lib/asm-commons-7.0_BETA.jar:lib/asm-tree-7.0_BETA.jar:lib/asm-analysis-7.0_BETA.jar:lib/guava-21.0.jar:out/lockedregioncodeinjection.jar \
  *     lockedregioncodeinjection.Main \
  *     -i out/test_input.jar -o out/test_output.jar \
  *     --targets 'Llockedregioncodeinjection/TestTarget;' \