Merge "Update the IME nav UI when gesture navigation mode changes"
diff --git a/Android.bp b/Android.bp
index 72bd642..8d82b38 100644
--- a/Android.bp
+++ b/Android.bp
@@ -178,7 +178,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-auxiliary.impl",
                 "framework-supplementalapi.impl",
             ],
         },
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ca211c1..3d6b52f 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -74,39 +74,21 @@
 
 stubs_defaults {
     name: "framework-doc-stubs-default",
+    defaults: ["android-non-updatable-stubs-defaults"],
     srcs: [
-        ":android-non-updatable-stub-sources",
-
         // No longer part of the stubs, but are included in the docs.
         ":android-test-base-sources",
         ":android-test-mock-sources",
         ":android-test-runner-sources",
     ],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
     libs: framework_docs_only_libs,
     create_doc_stubs: true,
-    annotations_enabled: true,
-    filter_packages: packages_to_document,
     api_levels_annotations_enabled: true,
     api_levels_annotations_dirs: [
         "sdk-dir",
         "api-versions-jars-dir",
     ],
-    previous_api: ":android.api.public.latest",
-    merge_annotations_dirs: [
-        "metalava-manual",
-    ],
     write_sdk_values: true,
-    // TODO(b/169090544): remove below aidl includes.
-    aidl: {
-        local_include_dirs: ["media/aidl"],
-        include_dirs: [
-            "frameworks/av/aidl",
-            "frameworks/native/libs/permission/aidl",
-        ],
-    },
 }
 
 // Defaults module for doc-stubs targets that use module source code as input.
@@ -119,7 +101,6 @@
         ":i18n.module.public.api{.public.stubs.source}",
 
         ":framework-appsearch-sources",
-        ":framework-auxiliary-sources",
         ":framework-connectivity-sources",
         ":framework-connectivity-tiramisu-updatable-sources",
         ":framework-graphics-srcs",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7f7380a..8a15758 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -286,7 +286,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
@@ -304,7 +303,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
@@ -337,7 +335,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
@@ -366,7 +363,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
@@ -383,7 +379,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
@@ -416,7 +411,6 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-auxiliary.stubs",
                 "framework-supplementalapi.stubs",
             ],
         },
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index e873514..67a3380 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -218,7 +218,7 @@
             })
 
         override fun parseImpl(file: File) =
-                parser.parsePackage(input.get()!!.reset(), file, 0).result
+                parser.parsePackage(input.get()!!.reset(), file, 0, null).result
                         as ParsingPackageImpl
     }
 
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index 1be68f5..4ad015d 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -20,7 +20,6 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Point;
 import android.os.RemoteException;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
@@ -133,12 +132,10 @@
         final WindowManager.LayoutParams mParams;
         final int mWidth;
         final int mHeight;
-        final Point mOutSurfaceSize = new Point();
         final SurfaceControl mOutSurfaceControl;
 
         final IntSupplier mViewVisibility;
 
-        int mFrameNumber;
         int mFlags;
 
         RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) {
@@ -155,9 +152,8 @@
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             while (state.keepRunning()) {
                 session.relayout(mWindow, mParams, mWidth, mHeight,
-                        mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
-                        mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
-                        mOutSurfaceSize);
+                        mViewVisibility.getAsInt(), mFlags, mOutFrames,
+                        mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls);
             }
         }
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 0ad70e4..ebf42b8 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -183,7 +183,7 @@
             COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
             COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
             COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
-            COMPRESS_TIME ? 32 * ONE_MINUTE : 3 * ONE_DAY
+            COMPRESS_TIME ? 32 * ONE_MINUTE : 8 * ONE_DAY
     };
 
     /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
diff --git a/api/Android.bp b/api/Android.bp
index 9428fcc..a22c2f6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -128,7 +128,6 @@
         "i18n.module.public.api",
     ],
     conditional_bootclasspath: [
-        "framework-auxiliary",
         "framework-supplementalapi",
     ],
     system_server_classpath: [
diff --git a/boot/Android.bp b/boot/Android.bp
index 8958d70..3273f2c 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -56,10 +56,6 @@
             module: "art-bootclasspath-fragment",
         },
         {
-            apex: "com.android.auxiliary",
-            module: "com.android.auxiliary-bootclasspath-fragment",
-        },
-        {
             apex: "com.android.conscrypt",
             module: "com.android.conscrypt-bootclasspath-fragment",
         },
diff --git a/core/api/current.txt b/core/api/current.txt
index 86fbcee..7363c26 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -134,6 +134,9 @@
     field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
     field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
     field public static final String READ_LOGS = "android.permission.READ_LOGS";
+    field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO";
+    field public static final String READ_MEDIA_IMAGE = "android.permission.READ_MEDIA_IMAGE";
+    field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO";
     field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
     field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
     field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
@@ -215,6 +218,8 @@
     field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES";
     field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS";
     field public static final String PHONE = "android.permission-group.PHONE";
+    field public static final String READ_MEDIA_AURAL = "android.permission-group.READ_MEDIA_AURAL";
+    field public static final String READ_MEDIA_VISUAL = "android.permission-group.READ_MEDIA_VISUAL";
     field public static final String SENSORS = "android.permission-group.SENSORS";
     field public static final String SMS = "android.permission-group.SMS";
     field public static final String STORAGE = "android.permission-group.STORAGE";
@@ -7296,10 +7301,10 @@
     method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
     method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
     method public CharSequence getDeviceOwnerLockScreenInfo();
-    method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
-    method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
-    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
-    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
     method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
     method @NonNull public String getEnrollmentSpecificId();
     method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -7343,6 +7348,7 @@
     method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
     method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
     method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName);
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig();
     method public int getRequiredPasswordComplexity();
     method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
@@ -7487,6 +7493,7 @@
     method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean);
+    method public void setPreferentialNetworkServiceConfig(@NonNull android.app.admin.PreferentialNetworkServiceConfig);
     method public void setPreferentialNetworkServiceEnabled(boolean);
     method public void setProfileEnabled(@NonNull android.content.ComponentName);
     method public void setProfileName(@NonNull android.content.ComponentName, String);
@@ -7710,29 +7717,29 @@
     ctor public DevicePolicyResources();
   }
 
-  public static final class DevicePolicyResources.Drawable {
-    field public static final int INVALID_ID = -1; // 0xffffffff
-    field public static final int WORK_PROFILE_ICON = 1; // 0x1
-    field public static final int WORK_PROFILE_ICON_BADGE = 0; // 0x0
-    field public static final int WORK_PROFILE_OFF_ICON = 2; // 0x2
-    field public static final int WORK_PROFILE_USER_ICON = 3; // 0x3
+  public static final class DevicePolicyResources.Drawables {
+    field public static final String UNDEFINED = "UNDEFINED";
+    field public static final String WORK_PROFILE_ICON = "WORK_PROFILE_ICON";
+    field public static final String WORK_PROFILE_ICON_BADGE = "WORK_PROFILE_ICON_BADGE";
+    field public static final String WORK_PROFILE_OFF_ICON = "WORK_PROFILE_OFF_ICON";
+    field public static final String WORK_PROFILE_USER_ICON = "WORK_PROFILE_USER_ICON";
   }
 
-  public static final class DevicePolicyResources.Drawable.Source {
-    field public static final int HOME_WIDGET = 2; // 0x2
-    field public static final int LAUNCHER_OFF_BUTTON = 3; // 0x3
-    field public static final int NOTIFICATION = 0; // 0x0
-    field public static final int PROFILE_SWITCH_ANIMATION = 1; // 0x1
-    field public static final int QUICK_SETTINGS = 4; // 0x4
-    field public static final int STATUS_BAR = 5; // 0x5
-    field public static final int UNDEFINED = -1; // 0xffffffff
+  public static final class DevicePolicyResources.Drawables.Source {
+    field public static final String HOME_WIDGET = "HOME_WIDGET";
+    field public static final String LAUNCHER_OFF_BUTTON = "LAUNCHER_OFF_BUTTON";
+    field public static final String NOTIFICATION = "NOTIFICATION";
+    field public static final String PROFILE_SWITCH_ANIMATION = "PROFILE_SWITCH_ANIMATION";
+    field public static final String QUICK_SETTINGS = "QUICK_SETTINGS";
+    field public static final String STATUS_BAR = "STATUS_BAR";
+    field public static final String UNDEFINED = "UNDEFINED";
   }
 
-  public static final class DevicePolicyResources.Drawable.Style {
-    field public static final int DEFAULT = -1; // 0xffffffff
-    field public static final int OUTLINE = 2; // 0x2
-    field public static final int SOLID_COLORED = 0; // 0x0
-    field public static final int SOLID_NOT_COLORED = 1; // 0x1
+  public static final class DevicePolicyResources.Drawables.Style {
+    field public static final String DEFAULT = "DEFAULT";
+    field public static final String OUTLINE = "OUTLINE";
+    field public static final String SOLID_COLORED = "SOLID_COLORED";
+    field public static final String SOLID_NOT_COLORED = "SOLID_NOT_COLORED";
   }
 
   public final class DnsEvent extends android.app.admin.NetworkEvent implements android.os.Parcelable {
@@ -7772,6 +7779,32 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.NetworkEvent> CREATOR;
   }
 
+  public final class PreferentialNetworkServiceConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public int[] getExcludedUids();
+    method @NonNull public int[] getIncludedUids();
+    method public int getNetworkId();
+    method public boolean isEnabled();
+    method public boolean isFallbackToDefaultConnectionAllowed();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.PreferentialNetworkServiceConfig> CREATOR;
+    field public static final int PREFERENTIAL_NETWORK_ID_1 = 1; // 0x1
+    field public static final int PREFERENTIAL_NETWORK_ID_2 = 2; // 0x2
+    field public static final int PREFERENTIAL_NETWORK_ID_3 = 3; // 0x3
+    field public static final int PREFERENTIAL_NETWORK_ID_4 = 4; // 0x4
+    field public static final int PREFERENTIAL_NETWORK_ID_5 = 5; // 0x5
+  }
+
+  public static final class PreferentialNetworkServiceConfig.Builder {
+    ctor public PreferentialNetworkServiceConfig.Builder();
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig build();
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setEnabled(boolean);
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setExcludedUids(@NonNull int[]);
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setFallbackToDefaultConnectionAllowed(boolean);
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setIncludedUids(@NonNull int[]);
+    method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setNetworkId(int);
+  }
+
   public class SecurityLog {
     ctor public SecurityLog();
     field public static final int LEVEL_ERROR = 3; // 0x3
@@ -8867,6 +8900,7 @@
     method @Deprecated public static android.bluetooth.BluetoothAdapter getDefaultAdapter();
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public java.time.Duration getDiscoverableTimeout();
     method public int getLeMaximumAdvertisingDataLength();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getMaxConnectedAudioDevices();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int);
     method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
@@ -9384,6 +9418,7 @@
     field public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
     field public static final String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
     field public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
+    field public static final String EXTRA_TRANSPORT = "android.bluetooth.device.extra.TRANSPORT";
     field public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
     field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
     field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
@@ -9753,18 +9788,54 @@
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothLeAudioCodecConfig {
+  public final class BluetoothLeAudioCodecConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBitsPerSample();
+    method public int getChannelMode();
     method @NonNull public String getCodecName();
+    method public int getCodecPriority();
     method public int getCodecType();
+    method public int getFrameDuration();
     method public static int getMaxCodecType();
+    method public int getOctetsPerFrame();
+    method public int getSampleRate();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1
+    field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2
+    field public static final int BITS_PER_SAMPLE_32 = 3; // 0x3
+    field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0
+    field public static final int CHANNEL_MODE_MONO = 1; // 0x1
+    field public static final int CHANNEL_MODE_NONE = 0; // 0x0
+    field public static final int CHANNEL_MODE_STEREO = 2; // 0x2
+    field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff
+    field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeAudioCodecConfig> CREATOR;
+    field public static final int FRAME_DURATION_10000 = 2; // 0x2
+    field public static final int FRAME_DURATION_7500 = 1; // 0x1
+    field public static final int FRAME_DURATION_NONE = 0; // 0x0
+    field public static final int SAMPLE_RATE_16000 = 2; // 0x2
+    field public static final int SAMPLE_RATE_24000 = 3; // 0x3
+    field public static final int SAMPLE_RATE_32000 = 4; // 0x4
+    field public static final int SAMPLE_RATE_44100 = 5; // 0x5
+    field public static final int SAMPLE_RATE_48000 = 6; // 0x6
+    field public static final int SAMPLE_RATE_8000 = 1; // 0x1
+    field public static final int SAMPLE_RATE_NONE = 0; // 0x0
     field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
     field public static final int SOURCE_CODEC_TYPE_LC3 = 0; // 0x0
   }
 
   public static final class BluetoothLeAudioCodecConfig.Builder {
     ctor public BluetoothLeAudioCodecConfig.Builder();
+    ctor public BluetoothLeAudioCodecConfig.Builder(@NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig build();
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setBitsPerSample(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setChannelMode(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecPriority(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecType(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setFrameDuration(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setOctetsPerFrame(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setSampleRate(int);
   }
 
   public final class BluetoothManager {
@@ -26177,6 +26248,15 @@
 
 package android.media.tv {
 
+  public final class AitInfo implements android.os.Parcelable {
+    ctor public AitInfo(int, int);
+    method public int describeContents();
+    method public int getType();
+    method public int getVersion();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.AitInfo> CREATOR;
+  }
+
   public final class TvContentRating {
     method public boolean contains(@NonNull android.media.tv.TvContentRating);
     method public static android.media.tv.TvContentRating createRating(String, String, String, java.lang.String...);
@@ -26747,12 +26827,14 @@
   public abstract static class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session(android.content.Context);
     method public void layoutSurface(int, int, int, int);
+    method public void notifyAitInfoUpdated(@NonNull android.media.tv.AitInfo);
     method public void notifyChannelRetuned(android.net.Uri);
     method public void notifyContentAllowed();
     method public void notifyContentBlocked(@NonNull android.media.tv.TvContentRating);
     method public void notifyTimeShiftStatusChanged(int);
     method public void notifyTrackSelected(int, String);
     method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
+    method public void notifyTuned(@NonNull android.net.Uri);
     method public void notifyVideoAvailable();
     method public void notifyVideoUnavailable(int);
     method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
@@ -26766,6 +26848,7 @@
     method public abstract void onRelease();
     method public boolean onSelectTrack(int, @Nullable String);
     method public abstract void onSetCaptionEnabled(boolean);
+    method public void onSetInteractiveAppNotificationEnabled(boolean);
     method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
     method public abstract boolean onSetSurface(@Nullable android.view.Surface);
     method public void onSurfaceChanged(int, int, int);
@@ -26867,6 +26950,7 @@
     method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
     method public void setCallback(@Nullable android.media.tv.TvView.TvInputCallback);
     method public void setCaptionEnabled(boolean);
+    method public void setInteractiveAppNotificationEnabled(boolean);
     method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
     method public void setStreamVolume(@FloatRange(from=0.0, to=1.0) float);
     method public void setTimeShiftPositionCallback(@Nullable android.media.tv.TvView.TimeShiftPositionCallback);
@@ -26893,6 +26977,7 @@
 
   public abstract static class TvView.TvInputCallback {
     ctor public TvView.TvInputCallback();
+    method public void onAitInfoUpdated(@NonNull String, @NonNull android.media.tv.AitInfo);
     method public void onChannelRetuned(String, android.net.Uri);
     method public void onConnectionFailed(String);
     method public void onContentAllowed(String);
@@ -26925,26 +27010,76 @@
 
   public final class TvInteractiveAppManager {
     method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList();
+    method public void registerCallback(@NonNull android.media.tv.interactive.TvInteractiveAppManager.TvInteractiveAppCallback, @NonNull java.util.concurrent.Executor);
+    method public void unregisterCallback(@NonNull android.media.tv.interactive.TvInteractiveAppManager.TvInteractiveAppCallback);
+    field public static final int ERROR_BLOCKED = 5; // 0x5
+    field public static final int ERROR_ENCRYPTED = 6; // 0x6
+    field public static final int ERROR_NONE = 0; // 0x0
+    field public static final int ERROR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_RESOURCE_UNAVAILABLE = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 1; // 0x1
+    field public static final int ERROR_UNKNOWN_CHANNEL = 7; // 0x7
+    field public static final int ERROR_WEAK_SIGNAL = 3; // 0x3
+    field public static final int INTERACTIVE_APP_STATE_ERROR = 3; // 0x3
+    field public static final int INTERACTIVE_APP_STATE_RUNNING = 2; // 0x2
+    field public static final int INTERACTIVE_APP_STATE_STOPPED = 1; // 0x1
+    field public static final int SERVICE_STATE_ERROR = 4; // 0x4
+    field public static final int SERVICE_STATE_PREPARING = 2; // 0x2
+    field public static final int SERVICE_STATE_READY = 3; // 0x3
+    field public static final int SERVICE_STATE_UNREALIZED = 1; // 0x1
+  }
+
+  public abstract static class TvInteractiveAppManager.TvInteractiveAppCallback {
+    ctor public TvInteractiveAppManager.TvInteractiveAppCallback();
+    method public void onTvInteractiveAppServiceStateChanged(@NonNull String, int, int, int);
   }
 
   public abstract class TvInteractiveAppService extends android.app.Service {
     ctor public TvInteractiveAppService();
+    method public final void notifyStateChanged(int, int, int);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method @Nullable public abstract android.media.tv.interactive.TvInteractiveAppService.Session onCreateSession(@NonNull String, int);
+    method public abstract void onPrepare(int);
     field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvInteractiveAppService";
     field public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
   }
 
+  public abstract static class TvInteractiveAppService.Session implements android.view.KeyEvent.Callback {
+    ctor public TvInteractiveAppService.Session(@NonNull android.content.Context);
+    method public void layoutSurface(int, int, int, int);
+    method public final void notifyBiInteractiveAppCreated(@NonNull android.net.Uri, @Nullable String);
+    method public void notifySessionStateChanged(int, int);
+    method public void onCreateBiInteractiveApp(@NonNull android.net.Uri, @Nullable android.os.Bundle);
+    method public void onDestroyBiInteractiveApp(@NonNull String);
+    method public boolean onKeyDown(int, @NonNull android.view.KeyEvent);
+    method public boolean onKeyLongPress(int, @NonNull android.view.KeyEvent);
+    method public boolean onKeyMultiple(int, int, @NonNull android.view.KeyEvent);
+    method public boolean onKeyUp(int, @NonNull android.view.KeyEvent);
+    method public abstract boolean onSetSurface(@Nullable android.view.Surface);
+    method public void onStartInteractiveApp();
+    method public void onStopInteractiveApp();
+    method public void onSurfaceChanged(int, int, int);
+    method public void onTuned(@NonNull android.net.Uri);
+  }
+
   public class TvInteractiveAppView extends android.view.ViewGroup {
     ctor public TvInteractiveAppView(@NonNull android.content.Context);
     ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
     ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
     method public void clearCallback();
+    method public void createBiInteractiveApp(@NonNull android.net.Uri, @Nullable android.os.Bundle);
+    method public void destroyBiInteractiveApp(@NonNull String);
+    method public void prepareInteractiveApp(@NonNull String, int);
     method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback);
+    method public int setTvView(@Nullable android.media.tv.TvView);
     method public void startInteractiveApp();
+    method public void stopInteractiveApp();
   }
 
   public abstract static class TvInteractiveAppView.TvInteractiveAppCallback {
     ctor public TvInteractiveAppView.TvInteractiveAppCallback();
+    method public void onBiInteractiveAppCreated(@NonNull String, @NonNull android.net.Uri, @Nullable String);
+    method public void onStateChanged(@NonNull String, int, int);
   }
 
 }
@@ -48383,6 +48518,7 @@
     method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
     method public default int getBufferTransformHint();
     method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
+    method public default void setTouchableRegion(@Nullable android.graphics.Region);
   }
 
   @UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
@@ -55127,6 +55263,7 @@
   public abstract class WebSettings {
     ctor public WebSettings();
     method @Deprecated public abstract boolean enableSmoothTransition();
+    method public boolean getAllowAlgorithmicDarkening();
     method public abstract boolean getAllowContentAccess();
     method public abstract boolean getAllowFileAccess();
     method public abstract boolean getAllowFileAccessFromFileURLs();
@@ -55148,7 +55285,7 @@
     method public abstract boolean getDomStorageEnabled();
     method public abstract String getFantasyFontFamily();
     method public abstract String getFixedFontFamily();
-    method public int getForceDark();
+    method @Deprecated public int getForceDark();
     method public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
     method public abstract boolean getJavaScriptEnabled();
     method public abstract android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
@@ -55171,6 +55308,7 @@
     method public abstract int getTextZoom();
     method public abstract boolean getUseWideViewPort();
     method public abstract String getUserAgentString();
+    method public void setAllowAlgorithmicDarkening(boolean);
     method public abstract void setAllowContentAccess(boolean);
     method public abstract void setAllowFileAccess(boolean);
     method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean);
@@ -55192,7 +55330,7 @@
     method @Deprecated public abstract void setEnableSmoothTransition(boolean);
     method public abstract void setFantasyFontFamily(String);
     method public abstract void setFixedFontFamily(String);
-    method public void setForceDark(int);
+    method @Deprecated public void setForceDark(int);
     method @Deprecated public abstract void setGeolocationDatabasePath(String);
     method public abstract void setGeolocationEnabled(boolean);
     method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
@@ -55223,9 +55361,9 @@
     method public abstract void setUserAgentString(@Nullable String);
     method public abstract boolean supportMultipleWindows();
     method public abstract boolean supportZoom();
-    field public static final int FORCE_DARK_AUTO = 1; // 0x1
-    field public static final int FORCE_DARK_OFF = 0; // 0x0
-    field public static final int FORCE_DARK_ON = 2; // 0x2
+    field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+    field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
     field public static final int LOAD_CACHE_ELSE_NETWORK = 1; // 0x1
     field public static final int LOAD_CACHE_ONLY = 3; // 0x3
     field public static final int LOAD_DEFAULT = -1; // 0xffffffff
@@ -57390,8 +57528,8 @@
     method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
     method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
     method public void setViewVisibility(@IdRes int, int);
-    method public void showNext(@IdRes int);
-    method public void showPrevious(@IdRes int);
+    method @Deprecated public void showNext(@IdRes int);
+    method @Deprecated public void showPrevious(@IdRes int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
     field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 04fc64b..ee31e13 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -97,6 +97,7 @@
 
   public class BluetoothFrameworkInitializer {
     method public static void registerServiceWrappers();
+    method public static void setBinderCallsStatsInitializer(@NonNull java.util.function.Consumer<android.content.Context>);
     method public static void setBluetoothServiceManager(@NonNull android.os.BluetoothServiceManager);
   }
 
@@ -278,6 +279,10 @@
     method public int getResourceId();
   }
 
+  public class LocalSocket implements java.io.Closeable {
+    ctor public LocalSocket(@NonNull java.io.FileDescriptor);
+  }
+
   public class NetworkIdentity {
     method public int getOemManaged();
     method public int getRatType();
@@ -331,6 +336,20 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
   }
 
+  public class NetworkStatsCollection {
+    method @NonNull public java.util.Map<android.net.NetworkStatsCollection.Key,android.net.NetworkStatsHistory> getEntries();
+  }
+
+  public static final class NetworkStatsCollection.Builder {
+    ctor public NetworkStatsCollection.Builder(long);
+    method @NonNull public android.net.NetworkStatsCollection.Builder addEntry(@NonNull android.net.NetworkStatsCollection.Key, @NonNull android.net.NetworkStatsHistory);
+    method @NonNull public android.net.NetworkStatsCollection build();
+  }
+
+  public static class NetworkStatsCollection.Key {
+    ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int);
+  }
+
   public final class NetworkStatsHistory implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ce2feac..ea2a641 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -287,6 +287,7 @@
     field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
     field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
     field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
+    field public static final String SEND_LOST_MODE_LOCATION_UPDATES = "android.permission.SEND_LOST_MODE_LOCATION_UPDATES";
     field public static final String SEND_SAFETY_CENTER_UPDATE = "android.permission.SEND_SAFETY_CENTER_UPDATE";
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -320,6 +321,7 @@
     field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
+    field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
     field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
     field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
@@ -1021,13 +1023,13 @@
 package android.app.admin {
 
   public final class DevicePolicyDrawableResource implements android.os.Parcelable {
-    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, int, @DrawableRes int);
-    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, @DrawableRes int);
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, @NonNull String, @NonNull String, @NonNull String, @DrawableRes int);
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, @NonNull String, @NonNull String, @DrawableRes int);
     method public int describeContents();
     method @DrawableRes public int getCallingPackageResourceId();
-    method public int getDrawableId();
-    method public int getDrawableSource();
-    method public int getDrawableStyle();
+    method @NonNull public String getDrawableId();
+    method @NonNull public String getDrawableSource();
+    method @NonNull public String getDrawableStyle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR;
   }
@@ -1066,8 +1068,9 @@
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
-    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull int[]);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]);
+    method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>);
@@ -1079,6 +1082,7 @@
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
     field @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE) public static final String ACTION_ESTABLISH_NETWORK_CONNECTION = "android.app.action.ESTABLISH_NETWORK_CONNECTION";
+    field public static final String ACTION_LOST_MODE_LOCATION_UPDATE = "android.app.action.LOST_MODE_LOCATION_UPDATE";
     field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
     field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
@@ -1105,6 +1109,7 @@
     field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
     field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
     field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
+    field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -2373,9 +2378,11 @@
     field public static final int ACCESS_REJECTED = 2; // 0x2
     field public static final int ACCESS_UNKNOWN = 0; // 0x0
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+    field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_SWITCH_BUFFER_SIZE = "android.bluetooth.device.action.SWITCH_BUFFER_SIZE";
     field public static final String DEVICE_TYPE_DEFAULT = "Default";
     field public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
     field public static final String DEVICE_TYPE_WATCH = "Watch";
+    field public static final String EXTRA_LOW_LATENCY_BUFFER_SIZE = "android.bluetooth.device.extra.LOW_LATENCY_BUFFER_SIZE";
     field public static final int METADATA_COMPANION_APP = 4; // 0x4
     field public static final int METADATA_DEVICE_TYPE = 17; // 0x11
     field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
@@ -6795,6 +6802,7 @@
     method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
     method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+    method @Nullable public android.media.tv.tuner.frontend.FrontendStatusReadiness[] getFrontendStatusReadiness(@NonNull int[]);
     method @IntRange(from=0xffffffff) public int getMaxNumberOfFrontends(int);
     method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean hasUnusedFrontend(int);
     method public boolean isLowestPriority(int);
@@ -8039,6 +8047,16 @@
     method public boolean isLocked();
   }
 
+  public class FrontendStatusReadiness {
+    method public int getStatusReadiness();
+    method public int getStatusType();
+    field public static final int FRONTEND_STATUS_READINESS_STABLE = 3; // 0x3
+    field public static final int FRONTEND_STATUS_READINESS_UNAVAILABLE = 1; // 0x1
+    field public static final int FRONTEND_STATUS_READINESS_UNDEFINED = 0; // 0x0
+    field public static final int FRONTEND_STATUS_READINESS_UNSTABLE = 2; // 0x2
+    field public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED = 4; // 0x4
+  }
+
   public class Isdbs3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getCodeRateCapability();
     method public int getModulationCapability();
@@ -9933,6 +9951,8 @@
     method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
     method public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
     method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
+    method public int checkPermissionForStartDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+    method public void finishDataDelivery(@NonNull String, @NonNull android.content.AttributionSource);
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
     method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
@@ -15731,6 +15751,7 @@
     method @Deprecated public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean);
     method @Deprecated public abstract void setUserAgent(int);
     method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean);
+    field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L
   }
 
   public class WebStorage {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 68c69e5..0081234 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2344,11 +2344,11 @@
             Manifest.permission.USE_BIOMETRIC,
             Manifest.permission.ACTIVITY_RECOGNITION,
             Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
-            null,
+            Manifest.permission.READ_MEDIA_AUDIO,
             null, // no permission for OP_WRITE_MEDIA_AUDIO
-            null,
+            Manifest.permission.READ_MEDIA_VIDEO,
             null, // no permission for OP_WRITE_MEDIA_VIDEO
-            null,
+            Manifest.permission.READ_MEDIA_IMAGE,
             null, // no permission for OP_WRITE_MEDIA_IMAGES
             null, // no permission for OP_LEGACY_STORAGE
             null, // no permission for OP_ACCESS_ACCESSIBILITY
diff --git a/core/java/android/app/admin/DevicePolicyDrawableResource.java b/core/java/android/app/admin/DevicePolicyDrawableResource.java
index d32ff84..61ff11b 100644
--- a/core/java/android/app/admin/DevicePolicyDrawableResource.java
+++ b/core/java/android/app/admin/DevicePolicyDrawableResource.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.admin.DevicePolicyResources.Drawables;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,9 +35,9 @@
  */
 @SystemApi
 public final class DevicePolicyDrawableResource implements Parcelable {
-    private final @DevicePolicyResources.UpdatableDrawableId int mDrawableId;
-    private final @DevicePolicyResources.UpdatableDrawableStyle int mDrawableStyle;
-    private final @DevicePolicyResources.UpdatableDrawableSource int mDrawableSource;
+    @NonNull private final @DevicePolicyResources.UpdatableDrawableId String mDrawableId;
+    @NonNull private final @DevicePolicyResources.UpdatableDrawableStyle String mDrawableStyle;
+    @NonNull private final @DevicePolicyResources.UpdatableDrawableSource String mDrawableSource;
     private final @DrawableRes int mCallingPackageResourceId;
     @NonNull private ParcelableResource mResource;
 
@@ -59,9 +60,9 @@
      */
     public DevicePolicyDrawableResource(
             @NonNull Context context,
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
-            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource,
             @DrawableRes int callingPackageResourceId) {
         this(drawableId, drawableStyle, drawableSource, callingPackageResourceId,
                 new ParcelableResource(context, callingPackageResourceId,
@@ -69,11 +70,17 @@
     }
 
     private DevicePolicyDrawableResource(
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
-            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource,
             @DrawableRes int callingPackageResourceId,
             @NonNull ParcelableResource resource) {
+
+        Objects.requireNonNull(drawableId);
+        Objects.requireNonNull(drawableStyle);
+        Objects.requireNonNull(drawableSource);
+        Objects.requireNonNull(resource);
+
         this.mDrawableId = drawableId;
         this.mDrawableStyle = drawableStyle;
         this.mDrawableSource = drawableSource;
@@ -98,34 +105,37 @@
      */
     public DevicePolicyDrawableResource(
             @NonNull Context context,
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
             @DrawableRes int callingPackageResourceId) {
-       this(context, drawableId, drawableStyle, DevicePolicyResources.Drawable.Source.UNDEFINED,
+       this(context, drawableId, drawableStyle, Drawables.Source.UNDEFINED,
                callingPackageResourceId);
     }
 
     /**
      * Returns the ID of the drawable to update.
      */
+    @NonNull
     @DevicePolicyResources.UpdatableDrawableId
-    public int getDrawableId() {
+    public String getDrawableId() {
         return mDrawableId;
     }
 
     /**
      * Returns the style of the drawable to update
      */
+    @NonNull
     @DevicePolicyResources.UpdatableDrawableStyle
-    public int getDrawableStyle() {
+    public String getDrawableStyle() {
         return mDrawableStyle;
     }
 
     /**
      * Returns the source of the drawable to update.
      */
+    @NonNull
     @DevicePolicyResources.UpdatableDrawableSource
-    public int getDrawableSource() {
+    public String getDrawableSource() {
         return mDrawableSource;
     }
 
@@ -153,9 +163,9 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         DevicePolicyDrawableResource other = (DevicePolicyDrawableResource) o;
-        return mDrawableId == other.mDrawableId
-                && mDrawableStyle == other.mDrawableStyle
-                && mDrawableSource == other.mDrawableSource
+        return mDrawableId.equals(other.mDrawableId)
+                && mDrawableStyle.equals(other.mDrawableStyle)
+                && mDrawableSource.equals(other.mDrawableSource)
                 && mCallingPackageResourceId == other.mCallingPackageResourceId
                 && mResource.equals(other.mResource);
     }
@@ -173,9 +183,9 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mDrawableId);
-        dest.writeInt(mDrawableStyle);
-        dest.writeInt(mDrawableSource);
+        dest.writeString(mDrawableId);
+        dest.writeString(mDrawableStyle);
+        dest.writeString(mDrawableSource);
         dest.writeInt(mCallingPackageResourceId);
         dest.writeTypedObject(mResource, flags);
     }
@@ -184,9 +194,9 @@
             new Creator<DevicePolicyDrawableResource>() {
                 @Override
                 public DevicePolicyDrawableResource createFromParcel(Parcel in) {
-                    int drawableId = in.readInt();
-                    int drawableStyle = in.readInt();
-                    int drawableSource = in.readInt();
+                    String drawableId = in.readString();
+                    String drawableStyle = in.readString();
+                    String drawableSource = in.readString();
                     int callingPackageResourceId = in.readInt();
                     ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR);
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 96d037c..cbce8ac 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,9 +16,6 @@
 
 package android.app.admin;
 
-import static android.app.admin.DevicePolicyResources.Drawable.INVALID_ID;
-import static android.app.admin.DevicePolicyResources.Drawable.Source.UNDEFINED;
-
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest.permission;
@@ -42,6 +39,7 @@
 import android.app.Activity;
 import android.app.IServiceConnection;
 import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyResources.Drawables;
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -62,6 +60,7 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.nfc.NfcAdapter;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -99,6 +98,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.net.NetworkUtilsInternal;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
@@ -133,6 +133,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 // TODO(b/172376923) - add CarDevicePolicyManager examples below (or remove reference to it).
 /**
@@ -1795,6 +1796,28 @@
             "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
 
     /**
+     * Broadcast action: sent when there is a location update on a device in lost mode. This
+     * broadcast is explicitly sent to the device policy controller app only.
+     *
+     * @see DevicePolicyManager#sendLostModeLocationUpdate
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_LOST_MODE_LOCATION_UPDATE =
+            "android.app.action.LOST_MODE_LOCATION_UPDATE";
+
+    /**
+     * Extra used with {@link #ACTION_LOST_MODE_LOCATION_UPDATE} to send the location of a device
+     * in lost mode. Value is {@code Location}.
+     *
+     * @see DevicePolicyManager#sendLostModeLocationUpdate
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_LOST_MODE_LOCATION =
+            "android.app.extra.LOST_MODE_LOCATION";
+
+    /**
      * The ComponentName of the administrator component.
      *
      * @see #ACTION_ADD_DEVICE_ADMIN
@@ -5824,6 +5847,56 @@
     }
 
     /**
+     * Send a lost mode location update to the admin. This API is limited to organization-owned
+     * devices, which includes devices with a device owner or devices with a profile owner on an
+     * organization-owned managed profile.
+     *
+     * <p>The caller must hold the
+     * {@link android.Manifest.permission#SEND_LOST_MODE_LOCATION_UPDATES} permission.
+     *
+     * <p> Not for use by third-party applications.
+     *
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback A callback object that will inform the caller whether a lost mode location
+     *                 update was successfully sent
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES)
+    public void sendLostModeLocationUpdate(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> callback) {
+        throwIfParentInstance("sendLostModeLocationUpdate");
+        if (mService == null) {
+            executeCallback(AndroidFuture.completedFuture(false), executor, callback);
+            return;
+        }
+        try {
+            final AndroidFuture<Boolean> future = new AndroidFuture<>();
+            mService.sendLostModeLocationUpdate(future);
+            executeCallback(future, executor, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void executeCallback(AndroidFuture<Boolean> future,
+            @CallbackExecutor @NonNull Executor executor,
+            Consumer<Boolean> callback) {
+        future.whenComplete((result, error) -> executor.execute(() -> {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (error != null) {
+                    callback.accept(false);
+                } else {
+                    callback.accept(result);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }));
+    }
+
+    /**
      * Called by an application that is administering the device to set the
      * global proxy and exclusion list.
      * <p>
@@ -10682,6 +10755,9 @@
      * On fully-managed devices this method is unsupported because all traffic is considered
      * work traffic.
      *
+     * <p> This method enables preferential network service with a default configuration.
+     * To fine-tune the configuration, use {@link #setPreferentialNetworkServiceConfig) instead.
+     *
      * <p>This method can only be called by the profile owner of a managed profile.
      * @param enabled whether preferential network service should be enabled.
      * @throws SecurityException if the caller is not the profile owner.
@@ -10720,6 +10796,56 @@
     }
 
     /**
+     * Sets preferential network configuration on the work profile.
+     * {@see PreferentialNetworkServiceConfig}
+     *
+     * An example of a supported preferential network service is the Enterprise
+     * slice on 5G networks.
+     *
+     * By default, preferential network service is disabled on the work profile on supported
+     * carriers and devices. Admins can explicitly enable it with this API.
+     * On fully-managed devices this method is unsupported because all traffic is considered
+     * work traffic.
+     *
+     * <p>This method can only be called by the profile owner of a managed profile.
+     * @param preferentialNetworkServiceConfig preferential network configuration.
+     * @throws SecurityException if the caller is not the profile owner.
+     **/
+    public void setPreferentialNetworkServiceConfig(
+            @NonNull PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
+        throwIfParentInstance("setPreferentialNetworkServiceConfig");
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfig);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get preferential network configuration
+     * {@see PreferentialNetworkServiceConfig}
+     *
+     * <p>This method can be called by the profile owner of a managed profile.
+     *
+     * @return preferential network configuration.
+     * @throws SecurityException if the caller is not the profile owner.
+     */
+    public @NonNull PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() {
+        throwIfParentInstance("getPreferentialNetworkServiceConfig");
+        if (mService == null) {
+            return PreferentialNetworkServiceConfig.DEFAULT;
+        }
+        try {
+            return mService.getPreferentialNetworkServiceConfig();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * This method is mostly deprecated.
      * Most of the settings that still have an effect have dedicated setter methods or user
      * restrictions. See individual settings for details.
@@ -14738,17 +14864,17 @@
     /**
      * For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if
      * {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to
-     * {@link DevicePolicyResources.Drawable.Source#UNDEFINED}, it updates the drawable resource for
+     * {@link DevicePolicyResources.Drawables.Source#UNDEFINED}, it updates the drawable resource for
      * the combination of {@link DevicePolicyDrawableResource#getDrawableId()} and
      * {@link DevicePolicyDrawableResource#getDrawableStyle()}, (see
-     * {@link DevicePolicyResources.Drawable} and {@link DevicePolicyResources.Drawable.Style}) to
+     * {@link DevicePolicyResources.Drawables} and {@link DevicePolicyResources.Drawables.Style}) to
      * the drawable with ID {@link DevicePolicyDrawableResource#getCallingPackageResourceId()},
      * meaning any system UI surface calling {@link #getDrawable}
      * with {@code drawableId} and {@code drawableStyle} will get the new resource after this API
      * is called.
      *
      * <p>Otherwise, if {@link DevicePolicyDrawableResource#getDrawableSource()} is set (see
-     * {@link DevicePolicyResources.Drawable.Source}, it overrides any drawables that was set for
+     * {@link DevicePolicyResources.Drawables.Source}, it overrides any drawables that was set for
      * the same {@code drawableId} and {@code drawableStyle} for the provided source.
      *
      * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
@@ -14784,10 +14910,10 @@
 
     /**
      * Removes all updated drawables for the list of {@code drawableIds} (see
-     * {@link DevicePolicyResources.Drawable} that was previously set by calling
+     * {@link DevicePolicyResources.Drawables} that was previously set by calling
      * {@link #setDrawables}, meaning any subsequent calls to {@link #getDrawable} for the provided
-     * IDs with any {@link DevicePolicyResources.Drawable.Style} and any
-     * {@link DevicePolicyResources.Drawable.Source} will return the default drawable from
+     * IDs with any {@link DevicePolicyResources.Drawables.Style} and any
+     * {@link DevicePolicyResources.Drawables.Source} will return the default drawable from
      * {@code defaultDrawableLoader}.
      *
      * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
@@ -14799,7 +14925,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
-    public void resetDrawables(@NonNull int[] drawableIds) {
+    public void resetDrawables(@NonNull String[] drawableIds) {
         if (mService != null) {
             try {
                 mService.resetDrawables(drawableIds);
@@ -14811,19 +14937,19 @@
 
     /**
      * Returns the appropriate updated drawable for the {@code drawableId}
-     * (see {@link DevicePolicyResources.Drawable}), with style {@code drawableStyle}
-     * (see {@link DevicePolicyResources.Drawable.Style}) if one was set using
+     * (see {@link DevicePolicyResources.Drawables}), with style {@code drawableStyle}
+     * (see {@link DevicePolicyResources.Drawables.Style}) if one was set using
      * {@code setDrawables}, otherwise returns the drawable from {@code defaultDrawableLoader}.
      *
      * <p>Also returns the drawable from {@code defaultDrawableLoader} if
-     * {@link DevicePolicyResources.Drawable#INVALID_ID} was passed.
+     * {@link DevicePolicyResources.Drawables#UNDEFINED} was passed.
      *
      * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
      * {@link NullPointerException} is thrown.
      *
      * <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
      * set a different value use
-     * {@link #getDrawableForDensity(int, int, int, Callable)}.
+     * {@link #getDrawableForDensity(String, String, int, Callable)}.
      *
      * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
      * notified when a resource has been updated.
@@ -14838,16 +14964,18 @@
      */
     @NonNull
     public Drawable getDrawable(
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
             @NonNull Callable<Drawable> defaultDrawableLoader) {
-        return getDrawable(drawableId, drawableStyle, UNDEFINED, defaultDrawableLoader);
+        return getDrawable(
+                drawableId, drawableStyle, Drawables.Source.UNDEFINED, defaultDrawableLoader);
     }
 
     /**
-     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
-     * a {@code drawableSource} (see {@link DevicePolicyResources.Drawable.Source}) which
-     * could result in returning a different drawable than {@link #getDrawable(int, int, Callable)}
+     * Similar to {@link #getDrawable(String, String, Callable)}, but also accepts
+     * a {@code drawableSource} (see {@link DevicePolicyResources.Drawables.Source}) which
+     * could result in returning a different drawable than
+     * {@link #getDrawable(String, String, Callable)}
      * if an override was set for that specific source.
      *
      * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
@@ -14864,12 +14992,17 @@
      */
     @NonNull
     public Drawable getDrawable(
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
-            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource,
             @NonNull Callable<Drawable> defaultDrawableLoader) {
+
+        Objects.requireNonNull(drawableId, "drawableId can't be null");
+        Objects.requireNonNull(drawableStyle, "drawableStyle can't be null");
+        Objects.requireNonNull(drawableSource, "drawableSource can't be null");
         Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
-        if (drawableId == INVALID_ID) {
+
+        if (Drawables.UNDEFINED.equals(drawableId)) {
             return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
         }
         if (mService != null) {
@@ -14897,7 +15030,7 @@
     }
 
     /**
-     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
+     * Similar to {@link #getDrawable(String, String, Callable)}, but also accepts
      * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
      *
      * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
@@ -14916,20 +15049,20 @@
      */
     @NonNull
     public Drawable getDrawableForDensity(
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
             int density,
             @NonNull Callable<Drawable> defaultDrawableLoader) {
         return getDrawableForDensity(
                 drawableId,
                 drawableStyle,
-                UNDEFINED,
+                Drawables.Source.UNDEFINED,
                 density,
                 defaultDrawableLoader);
     }
 
      /**
-     * Similar to {@link #getDrawable(int, int, int, Callable)}, but also accepts
+     * Similar to {@link #getDrawable(String, String, String, Callable)}, but also accepts
      * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
      *
      * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
@@ -14949,13 +15082,18 @@
      */
     @NonNull
     public Drawable getDrawableForDensity(
-            @DevicePolicyResources.UpdatableDrawableId int drawableId,
-            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
-            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+            @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+            @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource,
             int density,
             @NonNull Callable<Drawable> defaultDrawableLoader) {
+
+        Objects.requireNonNull(drawableId, "drawableId can't be null");
+        Objects.requireNonNull(drawableStyle, "drawableStyle can't be null");
+        Objects.requireNonNull(drawableSource, "drawableSource can't be null");
         Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
-        if (drawableId == INVALID_ID) {
+
+        if (Drawables.UNDEFINED.equals(drawableId)) {
             return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
         }
         if (mService != null) {
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 46e2cce..2ad2010 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -128,7 +128,6 @@
 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.STATUS_BAR_WORK_ICON_ACCESSIBILITY;
 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.WORK_LOCK_ACCESSIBILITY;
 
-import android.annotation.IntDef;
 import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -153,12 +152,12 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            Drawable.INVALID_ID,
-            Drawable.WORK_PROFILE_ICON_BADGE,
-            Drawable.WORK_PROFILE_ICON,
-            Drawable.WORK_PROFILE_OFF_ICON,
-            Drawable.WORK_PROFILE_USER_ICON
+    @StringDef(value = {
+            Drawables.UNDEFINED,
+            Drawables.WORK_PROFILE_ICON_BADGE,
+            Drawables.WORK_PROFILE_ICON,
+            Drawables.WORK_PROFILE_OFF_ICON,
+            Drawables.WORK_PROFILE_USER_ICON
     })
     public @interface UpdatableDrawableId {}
 
@@ -169,10 +168,11 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            Drawable.Style.SOLID_COLORED,
-            Drawable.Style.SOLID_NOT_COLORED,
-            Drawable.Style.OUTLINE,
+    @StringDef(value = {
+            Drawables.Style.SOLID_COLORED,
+            Drawables.Style.SOLID_NOT_COLORED,
+            Drawables.Style.OUTLINE,
+            Drawables.Style.DEFAULT
     })
     public @interface UpdatableDrawableStyle {}
 
@@ -182,14 +182,14 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            Drawable.Source.UNDEFINED,
-            Drawable.Source.NOTIFICATION,
-            Drawable.Source.PROFILE_SWITCH_ANIMATION,
-            Drawable.Source.HOME_WIDGET,
-            Drawable.Source.LAUNCHER_OFF_BUTTON,
-            Drawable.Source.QUICK_SETTINGS,
-            Drawable.Source.STATUS_BAR
+    @StringDef(value = {
+            Drawables.Source.UNDEFINED,
+            Drawables.Source.NOTIFICATION,
+            Drawables.Source.PROFILE_SWITCH_ANIMATION,
+            Drawables.Source.HOME_WIDGET,
+            Drawables.Source.LAUNCHER_OFF_BUTTON,
+            Drawables.Source.QUICK_SETTINGS,
+            Drawables.Source.STATUS_BAR
     })
     public @interface UpdatableDrawableSource {}
 
@@ -265,44 +265,44 @@
     /**
      * Class containing the identifiers used to update device management-related system drawable.
      */
-    public static final class Drawable {
+    public static final class Drawables {
 
-        private Drawable() {
+        private Drawables() {
         }
 
         /**
          * An ID for any drawable that can't be updated.
          */
-        public static final int INVALID_ID = -1;
+        public static final String UNDEFINED = "UNDEFINED";
 
         /**
          * Specifically used to badge work profile app icons.
          */
-        public static final int WORK_PROFILE_ICON_BADGE = 0;
+        public static final String WORK_PROFILE_ICON_BADGE = "WORK_PROFILE_ICON_BADGE";
 
         /**
          * General purpose work profile icon (i.e. generic icon badging). For badging app icons
          * specifically, see {@link #WORK_PROFILE_ICON_BADGE}.
          */
-        public static final int WORK_PROFILE_ICON = 1;
+        public static final String WORK_PROFILE_ICON = "WORK_PROFILE_ICON";
 
         /**
          * General purpose icon representing the work profile off state.
          */
-        public static final int WORK_PROFILE_OFF_ICON = 2;
+        public static final String WORK_PROFILE_OFF_ICON = "WORK_PROFILE_OFF_ICON";
 
         /**
          * General purpose icon for the work profile user avatar.
          */
-        public static final int WORK_PROFILE_USER_ICON = 3;
+        public static final String WORK_PROFILE_USER_ICON = "WORK_PROFILE_USER_ICON";
 
         /**
          * @hide
          */
-        public static final Set<Integer> UPDATABLE_DRAWABLE_IDS = buildDrawablesSet();
+        public static final Set<String> UPDATABLE_DRAWABLE_IDS = buildDrawablesSet();
 
-        private static Set<Integer> buildDrawablesSet() {
-            Set<Integer> drawables = new HashSet<>();
+        private static Set<String> buildDrawablesSet() {
+            Set<String> drawables = new HashSet<>();
             drawables.add(WORK_PROFILE_ICON_BADGE);
             drawables.add(WORK_PROFILE_ICON);
             drawables.add(WORK_PROFILE_OFF_ICON);
@@ -323,48 +323,48 @@
              * A source identifier indicating that the updatable resource is used in a generic
              * undefined location.
              */
-            public static final int UNDEFINED = -1;
+            public static final String UNDEFINED = "UNDEFINED";
 
             /**
              * A source identifier indicating that the updatable drawable is used in notifications.
              */
-            public static final int NOTIFICATION = 0;
+            public static final String NOTIFICATION = "NOTIFICATION";
 
             /**
              * A source identifier indicating that the updatable drawable is used in a cross
              * profile switching animation.
              */
-            public static final int PROFILE_SWITCH_ANIMATION = 1;
+            public static final String PROFILE_SWITCH_ANIMATION = "PROFILE_SWITCH_ANIMATION";
 
             /**
              * A source identifier indicating that the updatable drawable is used in a work
              * profile home screen widget.
              */
-            public static final int HOME_WIDGET = 2;
+            public static final String HOME_WIDGET = "HOME_WIDGET";
 
             /**
              * A source identifier indicating that the updatable drawable is used in the launcher
              * turn off work button.
              */
-            public static final int LAUNCHER_OFF_BUTTON = 3;
+            public static final String LAUNCHER_OFF_BUTTON = "LAUNCHER_OFF_BUTTON";
 
             /**
              * A source identifier indicating that the updatable drawable is used in quick settings.
              */
-            public static final int QUICK_SETTINGS = 4;
+            public static final String QUICK_SETTINGS = "QUICK_SETTINGS";
 
             /**
              * A source identifier indicating that the updatable drawable is used in the status bar.
              */
-            public static final int STATUS_BAR = 5;
+            public static final String STATUS_BAR = "STATUS_BAR";
 
             /**
              * @hide
              */
-            public static final Set<Integer> UPDATABLE_DRAWABLE_SOURCES = buildSourcesSet();
+            public static final Set<String> UPDATABLE_DRAWABLE_SOURCES = buildSourcesSet();
 
-            private static Set<Integer> buildSourcesSet() {
-                Set<Integer> sources = new HashSet<>();
+            private static Set<String> buildSourcesSet() {
+                Set<String> sources = new HashSet<>();
                 sources.add(UNDEFINED);
                 sources.add(NOTIFICATION);
                 sources.add(PROFILE_SWITCH_ANIMATION);
@@ -390,31 +390,31 @@
              * A style identifier indicating that the updatable drawable should use the default
              * style.
              */
-            public static final int DEFAULT = -1;
+            public static final String DEFAULT = "DEFAULT";
 
             /**
              * A style identifier indicating that the updatable drawable has a solid color fill.
              */
-            public static final int SOLID_COLORED = 0;
+            public static final String SOLID_COLORED = "SOLID_COLORED";
 
             /**
              * A style identifier indicating that the updatable drawable has a solid non-colored
              * fill.
              */
-            public static final int SOLID_NOT_COLORED = 1;
+            public static final String SOLID_NOT_COLORED = "SOLID_NOT_COLORED";
 
             /**
              * A style identifier indicating that the updatable drawable is an outline.
              */
-            public static final int OUTLINE = 2;
+            public static final String OUTLINE = "OUTLINE";
 
             /**
              * @hide
              */
-            public static final Set<Integer> UPDATABLE_DRAWABLE_STYLES = buildStylesSet();
+            public static final Set<String> UPDATABLE_DRAWABLE_STYLES = buildStylesSet();
 
-            private static Set<Integer> buildStylesSet() {
-                Set<Integer> styles = new HashSet<>();
+            private static Set<String> buildStylesSet() {
+                Set<String> styles = new HashSet<>();
                 styles.add(DEFAULT);
                 styles.add(SOLID_COLORED);
                 styles.add(SOLID_NOT_COLORED);
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f663c17..7525d54 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -24,6 +24,7 @@
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.admin.ParcelableGranteeMap;
+import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
@@ -47,6 +48,7 @@
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.telephony.data.ApnSetting;
+import com.android.internal.infra.AndroidFuture;
 
 import java.util.List;
 
@@ -119,6 +121,8 @@
     FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who);
     boolean isFactoryResetProtectionPolicySupported();
 
+    void sendLostModeLocationUpdate(in AndroidFuture<boolean> future);
+
     ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
     ComponentName getGlobalProxyAdmin(int userHandle);
     void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
@@ -283,6 +287,10 @@
     void setPreferentialNetworkServiceEnabled(in boolean enabled);
     boolean isPreferentialNetworkServiceEnabled(int userHandle);
 
+    void setPreferentialNetworkServiceConfig(
+            in PreferentialNetworkServiceConfig preferentialNetworkServiceConfig);
+    PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig();
+
     void setLockTaskPackages(in ComponentName who, in String[] packages);
     String[] getLockTaskPackages(in ComponentName who);
     boolean isLockTaskPermitted(in String pkg);
@@ -543,8 +551,8 @@
 
     List<UserHandle> listForegroundAffiliatedUsers();
     void setDrawables(in List<DevicePolicyDrawableResource> drawables);
-    void resetDrawables(in int[] drawableIds);
-    ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource);
+    void resetDrawables(in String[] drawableIds);
+    ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource);
 
     void setStrings(in List<DevicePolicyStringResource> strings);
     void resetStrings(in String[] stringIds);
diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl b/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl
new file mode 100644
index 0000000..6b6ee7d
--- /dev/null
+++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.admin;
+
+parcelable PreferentialNetworkServiceConfig;
\ No newline at end of file
diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
new file mode 100644
index 0000000..2849139
--- /dev/null
+++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Network configuration to be set for the user profile
+ * {@see DevicePolicyManager#setPreferentialNetworkServiceConfig}.
+ */
+public final class PreferentialNetworkServiceConfig implements Parcelable {
+    final boolean mIsEnabled;
+    final int mNetworkId;
+    final boolean mAllowFallbackToDefaultConnection;
+    final int[] mIncludedUids;
+    final int[] mExcludedUids;
+
+    /** @hide */
+    public static final PreferentialNetworkServiceConfig DEFAULT =
+            (new PreferentialNetworkServiceConfig.Builder()).build();
+
+    /**
+     * Preferential network identifier 1.
+     */
+    public static final int PREFERENTIAL_NETWORK_ID_1 = 1;
+
+    /**
+     * Preferential network identifier 2.
+     */
+    public static final int PREFERENTIAL_NETWORK_ID_2 = 2;
+
+    /**
+     * Preferential network identifier 3.
+     */
+    public static final int PREFERENTIAL_NETWORK_ID_3 = 3;
+
+    /**
+     * Preferential network identifier 4.
+     */
+    public static final int PREFERENTIAL_NETWORK_ID_4 = 4;
+
+    /**
+     * Preferential network identifier 5.
+     */
+    public static final int PREFERENTIAL_NETWORK_ID_5 = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PREFERENTIAL_NETWORK_ID_" }, value = {
+            PREFERENTIAL_NETWORK_ID_1,
+            PREFERENTIAL_NETWORK_ID_2,
+            PREFERENTIAL_NETWORK_ID_3,
+            PREFERENTIAL_NETWORK_ID_4,
+            PREFERENTIAL_NETWORK_ID_5,
+    })
+
+    public @interface PreferentialNetworkPreferenceId {
+    }
+
+    private PreferentialNetworkServiceConfig(boolean isEnabled,
+            boolean allowFallbackToDefaultConnection, int[] includedUids,
+            int[] excludedUids, @PreferentialNetworkPreferenceId int networkId) {
+        mIsEnabled = isEnabled;
+        mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection;
+        mIncludedUids = includedUids;
+        mExcludedUids = excludedUids;
+        mNetworkId = networkId;
+    }
+
+    private PreferentialNetworkServiceConfig(Parcel in) {
+        mIsEnabled = in.readBoolean();
+        mAllowFallbackToDefaultConnection = in.readBoolean();
+        mNetworkId = in.readInt();
+        mIncludedUids = in.createIntArray();
+        mExcludedUids = in.createIntArray();
+    }
+
+    /**
+     * Is the preferential network enabled.
+     * @return true if enabled else false
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * is fallback to default network allowed. This boolean configures whether default connection
+     * (default internet or wifi) should be used or not if a preferential network service
+     * connection is not available.
+     * @return true if fallback is allowed, else false.
+     */
+    public boolean isFallbackToDefaultConnectionAllowed() {
+        return mAllowFallbackToDefaultConnection;
+    }
+
+    /**
+     * Get the array of uids that are applicable for the profile preference.
+     *
+     * {@see #getExcludedUids()}
+     * Included UIDs and Excluded UIDs can't both be non-empty.
+     * if both are empty, it means this request applies to all uids in the user profile.
+     * if included is not empty, then only included UIDs are applied.
+     * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+     * @return Array of uids applicable for the profile preference.
+     *      Empty array would mean that this request applies to all uids in the profile.
+     */
+    public @NonNull int[] getIncludedUids() {
+        return mIncludedUids;
+    }
+
+    /**
+     * Get the array of uids that are excluded for the profile preference.
+     *
+     * {@see #getIncludedUids()}
+     * Included UIDs and Excluded UIDs can't both be non-empty.
+     * if both are empty, it means this request applies to all uids in the user profile.
+     * if included is not empty, then only included UIDs are applied.
+     * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+     * @return Array of uids that are excluded for the profile preference.
+     *      Empty array would mean that this request applies to all uids in the profile.
+     */
+    public @NonNull int[] getExcludedUids() {
+        return mExcludedUids;
+    }
+
+    /**
+     * @return preference enterprise identifier.
+     * valid values starts from
+     * {@link #PREFERENTIAL_NETWORK_ID_1} to {@link #PREFERENTIAL_NETWORK_ID_5}.
+     * preference identifier is applicable only if preference network service is enabled
+     *
+     */
+    public @PreferentialNetworkPreferenceId int getNetworkId() {
+        return mNetworkId;
+    }
+
+    @Override
+    public String toString() {
+        return "PreferentialNetworkServiceConfig{"
+                + "mIsEnabled=" + isEnabled()
+                + "mAllowFallbackToDefaultConnection=" + isFallbackToDefaultConnectionAllowed()
+                + "mIncludedUids=" + mIncludedUids.toString()
+                + "mExcludedUids=" + mExcludedUids.toString()
+                + "mNetworkId=" + mNetworkId
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        final PreferentialNetworkServiceConfig that = (PreferentialNetworkServiceConfig) o;
+        return mIsEnabled == that.mIsEnabled
+                && mAllowFallbackToDefaultConnection == that.mAllowFallbackToDefaultConnection
+                && mNetworkId == that.mNetworkId
+                && Objects.equals(mIncludedUids, that.mIncludedUids)
+                && Objects.equals(mExcludedUids, that.mExcludedUids);
+    }
+
+    @Override
+    public int hashCode() {
+        return ((Objects.hashCode(mIsEnabled) * 17)
+                + (Objects.hashCode(mAllowFallbackToDefaultConnection) * 19)
+                + (Objects.hashCode(mIncludedUids) * 23)
+                + (Objects.hashCode(mExcludedUids) * 29)
+                + mNetworkId * 31);
+    }
+
+    /**
+     * Builder used to create {@link PreferentialNetworkServiceConfig} objects.
+     * Specify the preferred Network preference
+     */
+    public static final class Builder {
+        boolean mIsEnabled = false;
+        int mNetworkId = 0;
+        boolean mAllowFallbackToDefaultConnection = true;
+        int[] mIncludedUids = new int[0];
+        int[] mExcludedUids = new int[0];
+
+        /**
+         * Constructs an empty Builder with preferential network disabled by default.
+         */
+        public Builder() {}
+
+        /**
+         * Set the preferential network service enabled state.
+         * Default value is false.
+         * @param isEnabled  the desired network preference to use, true to enable else false
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public PreferentialNetworkServiceConfig.Builder setEnabled(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Set whether the default connection should be used as fallback.
+         * This boolean configures whether the default connection (default internet or wifi)
+         * should be used if a preferential network service connection is not available.
+         * Default value is true
+         * @param allowFallbackToDefaultConnection  true if fallback is allowed else false
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public PreferentialNetworkServiceConfig.Builder setFallbackToDefaultConnectionAllowed(
+                boolean allowFallbackToDefaultConnection) {
+            mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection;
+            return this;
+        }
+
+        /**
+         * Set the array of uids whose network access will go through this preferential
+         * network service.
+         * {@see #setExcludedUids(int[])}
+         * Included UIDs and Excluded UIDs can't both be non-empty.
+         * if both are empty, it means this request applies to all uids in the user profile.
+         * if included is not empty, then only included UIDs are applied.
+         * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+         * @param uids  array of included uids
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public PreferentialNetworkServiceConfig.Builder setIncludedUids(
+                @NonNull int[] uids) {
+            Objects.requireNonNull(uids);
+            mIncludedUids = uids;
+            return this;
+        }
+
+        /**
+         * Set the array of uids who are not allowed through this preferential
+         * network service.
+         * {@see #setIncludedUids(int[])}
+         * Included UIDs and Excluded UIDs can't both be non-empty.
+         * if both are empty, it means this request applies to all uids in the user profile.
+         * if included is not empty, then only included UIDs are applied.
+         * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+         * @param uids  array of excluded uids
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public PreferentialNetworkServiceConfig.Builder setExcludedUids(
+                @NonNull int[] uids) {
+            Objects.requireNonNull(uids);
+            mExcludedUids = uids;
+            return this;
+        }
+
+        /**
+         * Returns an instance of {@link PreferentialNetworkServiceConfig} created from the
+         * fields set on this builder.
+         */
+        @NonNull
+        public PreferentialNetworkServiceConfig build() {
+            if (mIncludedUids.length > 0 && mExcludedUids.length > 0) {
+                throw new IllegalStateException("Both includedUids and excludedUids "
+                        + "cannot be nonempty");
+            }
+            return new PreferentialNetworkServiceConfig(mIsEnabled,
+                    mAllowFallbackToDefaultConnection, mIncludedUids, mExcludedUids, mNetworkId);
+        }
+
+        /**
+         * Set the preferential network identifier.
+         * Valid values starts from {@link #PREFERENTIAL_NETWORK_ID_1} to
+         * {@link #PREFERENTIAL_NETWORK_ID_5}.
+         * preference identifier is applicable only if preferential network service is enabled.
+         * @param preferenceId  preference Id
+         * @return The builder to facilitate chaining.
+         */
+        @NonNull
+        public PreferentialNetworkServiceConfig.Builder setNetworkId(
+                @PreferentialNetworkPreferenceId int preferenceId) {
+            if ((preferenceId < PREFERENTIAL_NETWORK_ID_1)
+                    || (preferenceId > PREFERENTIAL_NETWORK_ID_5)) {
+                throw new IllegalArgumentException("Invalid preference identifier");
+            }
+            mNetworkId = preferenceId;
+            return this;
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        dest.writeBoolean(mIsEnabled);
+        dest.writeBoolean(mAllowFallbackToDefaultConnection);
+        dest.writeInt(mNetworkId);
+        dest.writeIntArray(mIncludedUids);
+        dest.writeIntArray(mExcludedUids);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<PreferentialNetworkServiceConfig> CREATOR =
+            new Creator<PreferentialNetworkServiceConfig>() {
+                @Override
+                public PreferentialNetworkServiceConfig[] newArray(int size) {
+                    return new PreferentialNetworkServiceConfig[size];
+                }
+
+                @Override
+                public PreferentialNetworkServiceConfig createFromParcel(
+                        @NonNull android.os.Parcel in) {
+                    return new PreferentialNetworkServiceConfig(in);
+                }
+            };
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index b4f2302..5584f45 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.os.Process.myUserHandle;
 import static android.os.Trace.TRACE_TAG_DATABASE;
 
 import android.annotation.NonNull;
@@ -738,7 +739,7 @@
     }
 
     boolean checkUser(int pid, int uid, Context context) {
-        int callingUserId = UserHandle.getUserId(uid);
+        final int callingUserId = UserHandle.getUserId(uid);
 
         if (callingUserId == context.getUserId() || mSingleUser) {
             return true;
@@ -765,8 +766,8 @@
                 if (um != null && um.isCloneProfile()) {
                     UserHandle parent = um.getProfileParent(callingUser);
 
-                    if (parent != null && parent.equals(context.getUser())) {
-                        mUsersRedirectedToOwner.put(callingUser.getIdentifier(), true);
+                    if (parent != null && parent.equals(myUserHandle())) {
+                        mUsersRedirectedToOwner.put(callingUserId, true);
                         return true;
                     }
                 }
@@ -774,7 +775,7 @@
                 // ignore
             }
 
-            mUsersRedirectedToOwner.put(UserHandle.getUserId(uid), false);
+            mUsersRedirectedToOwner.put(callingUserId, false);
             return false;
         }
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6ae768a..98ced6d 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -24,6 +24,9 @@
 import android.annotation.UiContext;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -47,12 +50,16 @@
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.autofill.AutofillManager.AutofillClient;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -66,6 +73,31 @@
     @UnsupportedAppUsage
     Context mBase;
 
+    /**
+     * After {@link Build.VERSION_CODES#TIRAMISU},
+     * {@link #registerComponentCallbacks(ComponentCallbacks)} will delegate to
+     * {@link #getBaseContext()} instead of {@link #getApplicationContext()}.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    @VisibleForTesting
+    static final long COMPONENT_CALLBACK_ON_WRAPPER = 193247900L;
+
+    /**
+     * A list to store {@link ComponentCallbacks} which
+     * passes to {@link #registerComponentCallbacks(ComponentCallbacks)} before
+     * {@link #attachBaseContext(Context)}.
+     * It is to provide compatibility behavior for Application targeted prior to
+     * {@link Build.VERSION_CODES#TIRAMISU}.
+     *
+     * @hide
+     */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    public List<ComponentCallbacks> mCallbacksRegisteredToSuper;
+
+    private final Object mLock = new Object();
+
     public ContextWrapper(Context base) {
         mBase = base;
     }
@@ -1301,4 +1333,77 @@
         }
         return mBase.isConfigurationContext();
     }
+
+    /**
+     * Add a new {@link ComponentCallbacks} to the base application of the
+     * Context, which will be called at the same times as the ComponentCallbacks
+     * methods of activities and other components are called. Note that you
+     * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
+     * appropriate in the future; this will not be removed for you.
+     * <p>
+     * After {@link Build.VERSION_CODES#TIRAMISU}, the {@link ComponentCallbacks} will be registered
+     * to {@link #getBaseContext() the base Context}, and can be only used after
+     * {@link #attachBaseContext(Context)}. Users can still call to
+     * {@code getApplicationContext().registerComponentCallbacks(ComponentCallbacks)} to add
+     * {@link ComponentCallbacks} to the base application.
+     *
+     * @param callback The interface to call.  This can be either a
+     * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
+     * @throws IllegalStateException if this method calls before {@link #attachBaseContext(Context)}
+     */
+    @Override
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        if (mBase != null) {
+            mBase.registerComponentCallbacks(callback);
+        } else if (!CompatChanges.isChangeEnabled(COMPONENT_CALLBACK_ON_WRAPPER)) {
+            super.registerComponentCallbacks(callback);
+            synchronized (mLock) {
+                // Also register ComponentCallbacks to ContextWrapper, so we can find the correct
+                // Context to unregister it for compatibility.
+                if (mCallbacksRegisteredToSuper == null) {
+                    mCallbacksRegisteredToSuper = new ArrayList<>();
+                }
+                mCallbacksRegisteredToSuper.add(callback);
+            }
+        } else {
+            // Throw exception for Application targeting T+
+            throw new IllegalStateException("ComponentCallbacks must be registered after "
+                    + "this ContextWrapper is attached to a base Context.");
+        }
+    }
+
+    /**
+     * Remove a {@link ComponentCallbacks} object that was previously registered
+     * with {@link #registerComponentCallbacks(ComponentCallbacks)}.
+     * <p>
+     * After {@link Build.VERSION_CODES#TIRAMISU}, the {@link ComponentCallbacks} will be
+     * unregistered to {@link #getBaseContext() the base Context}, and can be only used after
+     * {@link #attachBaseContext(Context)}
+     * </p>
+     *
+     * @param callback The interface to call.  This can be either a
+     * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
+     * @throws IllegalStateException if this method calls before {@link #attachBaseContext(Context)}
+     */
+    @Override
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        // It usually means the ComponentCallbacks is registered before this ContextWrapper attaches
+        // to a base Context and Application is targeting prior to S-v2. We should unregister the
+        // ComponentCallbacks to the Application Context instead to prevent leak.
+        synchronized (mLock) {
+            if (mCallbacksRegisteredToSuper != null
+                    && mCallbacksRegisteredToSuper.contains(callback)) {
+                super.unregisterComponentCallbacks(callback);
+                mCallbacksRegisteredToSuper.remove(callback);
+            } else if (mBase != null) {
+                mBase.unregisterComponentCallbacks(callback);
+            } else if (CompatChanges.isChangeEnabled(COMPONENT_CALLBACK_ON_WRAPPER)) {
+                // Throw exception for Application that is targeting S-v2+
+                throw new IllegalStateException("ComponentCallbacks must be unregistered after "
+                        + "this ContextWrapper is attached to a base Context.");
+            }
+        }
+        // Do nothing if the callback hasn't been registered to Application Context by
+        // super.unregisterComponentCallbacks() for Application that is targeting prior to T.
+    }
 }
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index 7f1d0d1..5bb845d 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -32,6 +32,9 @@
         },
         {
           "include-filter": "android.content.ComponentCallbacksControllerTest"
+        },
+        {
+          "include-filter": "android.content.ContextWrapperTest"
         }
       ],
       "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java", "(/|^)ComponentCallbacksController.java"]
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1021d3e..5b0c275 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -329,7 +329,8 @@
          * @hide
          */
         public Bundle toBundle(Bundle outBundle) {
-            final Bundle b = outBundle == null ? new Bundle() : outBundle;
+            final Bundle b = outBundle == null || outBundle == Bundle.EMPTY
+                    ? new Bundle() : outBundle;
             if (mType == TYPE_BOOLEAN) {
                 b.putBoolean(mName, mBooleanValue);
             } else if (mType == TYPE_FLOAT) {
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index d5498a0..165cae8 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -78,6 +78,7 @@
     public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
     private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
     private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
+    private static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
     private static final String TAG_APPLICATION = "application";
     private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
     private static final String TAG_PROFILEABLE = "profileable";
@@ -101,7 +102,7 @@
     public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
             File packageFile, int flags) {
         if (packageFile.isDirectory()) {
-            return parseClusterPackageLite(input, packageFile, flags);
+            return parseClusterPackageLite(input, packageFile, /* frameworkSplits= */ null, flags);
         } else {
             return parseMonolithicPackageLite(input, packageFile, flags);
         }
@@ -134,21 +135,44 @@
 
     /**
      * Parse lightweight details about a directory of APKs.
+     *
+     * @param packageDirOrApk is the folder that contains split apks for a regular app or the
+     *                        framework-res.apk for framwork-res splits (in which case the
+     *                        splits come in the <code>frameworkSplits</code> parameter)
      */
     public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
-            File packageDir, int flags) {
-        final File[] files = packageDir.listFiles();
-        if (ArrayUtils.isEmpty(files)) {
-            return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
-                    "No packages found in split");
+            File packageDirOrApk, List<File> frameworkSplits, int flags) {
+        final File[] files;
+        final boolean parsingFrameworkSplits = (flags & PARSE_FRAMEWORK_RES_SPLITS) != 0;
+        if (parsingFrameworkSplits) {
+            if (ArrayUtils.isEmpty(frameworkSplits)) {
+                return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+                        "No packages found in split");
+            }
+            files = frameworkSplits.toArray(new File[frameworkSplits.size() + 1]);
+            // we also want to process the base apk so add it to the array
+            files[files.length - 1] = packageDirOrApk;
+        } else {
+            files = packageDirOrApk.listFiles();
+            if (ArrayUtils.isEmpty(files)) {
+                return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+                        "No packages found in split");
+            }
+            // Apk directory is directly nested under the current directory
+            if (files.length == 1 && files[0].isDirectory()) {
+                return parseClusterPackageLite(input, files[0], frameworkSplits, flags);
+            }
         }
-        // Apk directory is directly nested under the current directory
-        if (files.length == 1 && files[0].isDirectory()) {
-            return parseClusterPackageLite(input, files[0], flags);
+
+        if (parsingFrameworkSplits) {
+            // disable the flag for checking the certificates of the splits. We know they
+            // won't match, but we rely on the mainline apex to be safe if it was installed
+            flags = flags & ~PARSE_COLLECT_CERTIFICATES;
         }
 
         String packageName = null;
         int versionCode = 0;
+        ApkLite baseApk = null;
 
         final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
@@ -161,6 +185,10 @@
                     }
 
                     final ApkLite lite = result.getResult();
+                    if (parsingFrameworkSplits && file == files[files.length - 1]) {
+                        baseApk = lite;
+                        break;
+                    }
                     // Assert that all package names and version codes are
                     // consistent with the first one we encounter.
                     if (packageName == null) {
@@ -172,7 +200,8 @@
                                     "Inconsistent package " + lite.getPackageName() + " in " + file
                                             + "; expected " + packageName);
                         }
-                        if (versionCode != lite.getVersionCode()) {
+                        // we allow version codes that do not match for framework splits
+                        if (!parsingFrameworkSplits && versionCode != lite.getVersionCode()) {
                             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                     "Inconsistent version " + lite.getVersionCode() + " in " + file
                                             + "; expected " + versionCode);
@@ -187,12 +216,15 @@
                     }
                 }
             }
+            // baseApk is set in the last iteration of the for each loop when we are parsing
+            // frameworkRes splits or needs to be done now otherwise
+            if (!parsingFrameworkSplits) {
+                baseApk = apks.remove(null);
+            }
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
-
-        final ApkLite baseApk = apks.remove(null);
-        return composePackageLiteFromApks(input, packageDir, baseApk, apks);
+        return composePackageLiteFromApks(input, packageDirOrApk, baseApk, apks);
     }
 
     /**
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index d1f49d2..506cbcb 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -55,7 +55,9 @@
      * Create a LocalServerSocket from a file descriptor that's already
      * been created and bound. listen() will be called immediately on it.
      * Used for cases where file descriptors are passed in via environment
-     * variables
+     * variables. The passed-in FileDescriptor is not managed by this class
+     * and must be closed by the caller. Calling {@link #close()} on a socket
+     * created by this method has no effect.
      *
      * @param fd bound file descriptor
      * @throws IOException
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 5b38f78..b69410c 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -16,7 +16,14 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.system.ErrnoException;
+import android.system.Os;
 
 import java.io.Closeable;
 import java.io.FileDescriptor;
@@ -74,32 +81,39 @@
         this.isBound = false;
     }
 
+    private void checkConnected() {
+        try {
+            Os.getpeername(impl.getFileDescriptor());
+        } catch (ErrnoException e) {
+            throw new IllegalArgumentException("Not a connected socket", e);
+        }
+        isConnected = true;
+        isBound = true;
+        implCreated = true;
+    }
+
     /**
-     * Creates a LocalSocket instances using the FileDescriptor for an already-connected
-     * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller:
-     * closing the LocalSocket will not close it.
+     * Creates a LocalSocket instance using the {@link FileDescriptor} for an already-connected
+     * AF_LOCAL/UNIX domain stream socket. The passed-in FileDescriptor is not managed by this class
+     * and must be closed by the caller. Calling {@link #close()} on a socket created by this
+     * method has no effect.
      *
-     * @hide - used by BluetoothSocket.
+     * @param fd the filedescriptor to adopt
+     *
+     * @hide
      */
-    public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) {
-        return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
+    @SystemApi(client = MODULE_LIBRARIES)
+    public LocalSocket(@NonNull @SuppressLint("UseParcelFileDescriptor") FileDescriptor fd) {
+        this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
+        checkConnected();
     }
 
     /**
      * for use with LocalServerSocket.accept()
      */
     static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
-        return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
-    }
-
-    /**
-     * Creates a LocalSocket from an existing LocalSocketImpl that is already connected.
-     */
-    private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
-        LocalSocket socket = new LocalSocket(impl, sockType);
-        socket.isConnected = true;
-        socket.isBound = true;
-        socket.implCreated = true;
+        LocalSocket socket = new LocalSocket(impl, SOCKET_UNKNOWN);
+        socket.checkConnected();
         return socket;
     }
 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d4a338b..1810904 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -44,6 +44,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -612,7 +613,7 @@
     public static final int WAKE_REASON_PLUGGED_IN = 3;
 
     /**
-     * Wake up reason code: Waking up due to a user performed gesture (e.g. douple tapping on the
+     * Wake up reason code: Waking up due to a user performed gesture (e.g. double tapping on the
      * screen).
      * @hide
      */
@@ -1013,7 +1014,7 @@
 
     private static final int MAX_CACHE_ENTRIES = 1;
 
-    private PropertyInvalidatedCache<Void, Boolean> mPowerSaveModeCache =
+    private final PropertyInvalidatedCache<Void, Boolean> mPowerSaveModeCache =
             new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
                 CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY) {
                 @Override
@@ -1026,7 +1027,7 @@
                 }
             };
 
-    private PropertyInvalidatedCache<Void, Boolean> mInteractiveCache =
+    private final PropertyInvalidatedCache<Void, Boolean> mInteractiveCache =
             new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
                 CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
                 @Override
@@ -1047,7 +1048,7 @@
     final IThermalService mThermalService;
 
     /** We lazily initialize it.*/
-    private PowerWhitelistManager mPowerWhitelistManager;
+    private PowerExemptionManager mPowerExemptionManager;
 
     private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
             mListenerMap = new ArrayMap<>();
@@ -1063,12 +1064,12 @@
         mHandler = handler;
     }
 
-    private PowerWhitelistManager getPowerWhitelistManager() {
-        if (mPowerWhitelistManager == null) {
+    private PowerExemptionManager getPowerExemptionManager() {
+        if (mPowerExemptionManager == null) {
             // No need for synchronization; getSystemService() will return the same object anyway.
-            mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class);
+            mPowerExemptionManager = mContext.getSystemService(PowerExemptionManager.class);
         }
-        return mPowerWhitelistManager;
+        return mPowerExemptionManager;
     }
 
     /**
@@ -1339,12 +1340,12 @@
     }
 
     /**
-     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
      * to turn off.
      *
-     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
      * turned on it will be turned off. If all displays are off as a result of this action the
-     * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+     * device will be put to sleep. If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP
      * default display group} is already off then nothing will happen.
      *
      * <p>If the device is an Android TV playback device and the current active source on the
@@ -1372,12 +1373,12 @@
     }
 
     /**
-     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
      * to turn off.
      *
-     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
      * turned on it will be turned off. If all displays are off as a result of this action the
-     * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
+     * device will be put to sleep. If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP
      * default display group} is already off then nothing will happen.
      *
      * <p>
@@ -1409,12 +1410,12 @@
     }
 
     /**
-     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
      * to turn on.
      *
-     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
      * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
      * on then nothing will happen.
      *
      * <p>
@@ -1440,12 +1441,12 @@
     }
 
     /**
-     * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group}
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
      * to turn on.
      *
-     * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is
+     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
      * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already
+     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
      * on then nothing will happen.
      *
      * <p>
@@ -2144,7 +2145,7 @@
      * features to the app. Guardrails for extreme cases may still be applied.
      */
     public boolean isIgnoringBatteryOptimizations(String packageName) {
-        return getPowerWhitelistManager().isWhitelisted(packageName, true);
+        return getPowerExemptionManager().isAllowListed(packageName, true);
     }
 
     /**
@@ -2270,8 +2271,8 @@
      * @param listener listener to be added,
      */
     public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
-        this.addThermalStatusListener(mContext.getMainExecutor(), listener);
+        Objects.requireNonNull(listener, "listener cannot be null");
+        addThermalStatusListener(mContext.getMainExecutor(), listener);
     }
 
     /**
@@ -2282,8 +2283,8 @@
      */
     public void addThermalStatusListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull OnThermalStatusChangedListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
-        Preconditions.checkNotNull(executor, "executor cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
         Preconditions.checkArgument(!mListenerMap.containsKey(listener),
                 "Listener already registered: %s", listener);
         IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
@@ -2291,9 +2292,7 @@
             public void onStatusChange(int status) {
                 final long token = Binder.clearCallingIdentity();
                 try {
-                    executor.execute(() -> {
-                        listener.onThermalStatusChanged(status);
-                    });
+                    executor.execute(() -> listener.onThermalStatusChanged(status));
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
@@ -2316,7 +2315,7 @@
      * @param listener listener to be removed
      */
     public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         IThermalStatusListener internalListener = mListenerMap.get(listener);
         Preconditions.checkArgument(internalListener != null, "Listener was not added");
         try {
@@ -2945,6 +2944,7 @@
          *
          * @hide
          */
+        @SuppressLint("WakelockTimeout")
         public Runnable wrap(Runnable r) {
             acquire();
             return () -> {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f18c9c9..bd65a41 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4787,33 +4787,6 @@
     }
 
     /**
-     * Immediately removes the user or, if the user cannot be removed, such as when the user is
-     * the current user, then set the user as ephemeral so that it will be removed when it is
-     * stopped.
-     *
-     * @param evenWhenDisallowed when {@code true}, user is removed even if the caller has the
-     * {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
-     *
-     * @return the {@link RemoveResult} code
-     *
-     * @deprecated  TODO(b/199446770): remove this call after converting all calls to
-     * removeUserWhenPossible(UserHandle, boolean)
-     *
-     * @hide
-     */
-    @Deprecated
-    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
-            Manifest.permission.CREATE_USERS})
-    public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
-            boolean evenWhenDisallowed) {
-        try {
-            return mService.removeUserWhenPossible(userId, evenWhenDisallowed);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Updates the user's name.
      *
      * @param userId the user's integer id
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e4aee76..15f13eb 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -33,6 +33,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.ChangeId;
@@ -239,6 +240,41 @@
     }
 
     /**
+     *
+     * Similar to checkPermissionForDataDelivery, except it results in an app op start, rather than
+     * a note. If this method is used, then {@link #finishDataDelivery(String, AttributionSource)}
+     * must be used when access is finished.
+     *
+     * @param permission The permission to check.
+     * @param attributionSource the permission identity
+     * @param message A message describing the reason the permission was checked
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkPermissionForDataDelivery(String, AttributionSource, String)
+     */
+    @PermissionCheckerManager.PermissionResult
+    public int checkPermissionForStartDataDelivery(@NonNull String permission,
+            @NonNull AttributionSource attributionSource, @Nullable String message) {
+        return PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
+                // FIXME(b/199526514): PID should be passed inside AttributionSource.
+                PermissionChecker.PID_UNKNOWN, attributionSource, message, true);
+    }
+
+    /**
+     * Indicate that usage has finished for an {@link AttributionSource} started with
+     * {@link #checkPermissionForStartDataDelivery(String, AttributionSource, String)}
+     *
+     * @param permission The permission to check.
+     * @param attributionSource the permission identity to finish
+     */
+    public void finishDataDelivery(@NonNull String permission,
+            @NonNull AttributionSource attributionSource) {
+        PermissionChecker.finishDataDelivery(mContext, AppOpsManager.permissionToOp(permission),
+                attributionSource);
+    }
+
+    /**
      * Checks whether a given data access chain described by the given {@link AttributionSource}
      * has a given permission. Call this method if you are the datasource which would not blame you
      * for access to the data since you are the data. Use this API if you are the datasource of the
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
similarity index 91%
rename from services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
rename to core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
index c26965d..c452e06 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.server.selectiontoolbar;
+package android.service.selectiontoolbar;
 
-import android.service.selectiontoolbar.SelectionToolbarRenderService;
 import android.util.Log;
 import android.view.selectiontoolbar.ShowInfo;
 
 /**
  * The default implementation of {@link SelectionToolbarRenderService}.
+ *
+ *  @hide
  */
+// TODO(b/214122495): fix class not found then move to system service folder
 public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService {
 
     private static final String TAG = "DefaultSelectionToolbarRenderService";
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
new file mode 100644
index 0000000..95ecc4e
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -0,0 +1,1619 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Size;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.Transformation;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.floatingtoolbar.FloatingToolbarPopup;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A popup window used by the floating toolbar to render menu items in the local app process.
+ *
+ * This class is responsible for the rendering/animation of the floating toolbar.
+ * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+ * to transition between panels.
+ */
+
+final class RemoteSelectionToolbar implements FloatingToolbarPopup {
+
+    /* Minimum and maximum number of items allowed in the overflow. */
+    private static final int MIN_OVERFLOW_SIZE = 2;
+    private static final int MAX_OVERFLOW_SIZE = 4;
+
+    private final Context mContext;
+    private final View mParent;  // Parent for the popup window.
+    private final PopupWindow mPopupWindow;
+
+    /* Margins between the popup window and its content. */
+    private final int mMarginHorizontal;
+    private final int mMarginVertical;
+
+    /* View components */
+    private final ViewGroup mContentContainer;  // holds all contents.
+    private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
+    // holds menu items hidden in the overflow.
+    private final OverflowPanel mOverflowPanel;
+    private final ImageButton mOverflowButton;  // opens/closes the overflow.
+    /* overflow button drawables. */
+    private final Drawable mArrow;
+    private final Drawable mOverflow;
+    private final AnimatedVectorDrawable mToArrow;
+    private final AnimatedVectorDrawable mToOverflow;
+
+    private final OverflowPanelViewHelper mOverflowPanelViewHelper;
+
+    /* Animation interpolators. */
+    private final Interpolator mLogAccelerateInterpolator;
+    private final Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mFastOutLinearInInterpolator;
+
+    /* Animations. */
+    private final AnimatorSet mShowAnimation;
+    private final AnimatorSet mDismissAnimation;
+    private final AnimatorSet mHideAnimation;
+    private final AnimationSet mOpenOverflowAnimation;
+    private final AnimationSet mCloseOverflowAnimation;
+    private final Animation.AnimationListener mOverflowAnimationListener;
+
+    private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
+    private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
+    /* Temporary data holders. Reset values before using. */
+    private final int[] mTmpCoords = new int[2];
+
+    private final Region mTouchableRegion = new Region();
+    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+            info -> {
+                info.contentInsets.setEmpty();
+                info.visibleInsets.setEmpty();
+                info.touchableRegion.set(mTouchableRegion);
+                info.setTouchableInsets(
+                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            };
+
+    private final int mLineHeight;
+    private final int mIconTextSpacing;
+
+    /**
+     * @see OverflowPanelViewHelper#preparePopupContent().
+     */
+    private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
+        @Override
+        public void run() {
+            setPanelsStatesAtRestingPosition();
+            setContentAreaAsTouchableSurface();
+            mContentContainer.setAlpha(1);
+        }
+    };
+
+    private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
+    private boolean mHidden; // tracks whether this popup is hidden or hiding.
+
+    /* Calculated sizes for panels and overflow button. */
+    private final Size mOverflowButtonSize;
+    private Size mOverflowPanelSize;  // Should be null when there is no overflow.
+    private Size mMainPanelSize;
+
+    /* Menu items and click listeners */
+    private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>();
+    private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
+    private final View.OnClickListener mMenuItemButtonOnClickListener =
+            new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mOnMenuItemClickListener == null) {
+                        return;
+                    }
+                    final Object tag = v.getTag();
+                    if (!(tag instanceof MenuItemRepr)) {
+                        return;
+                    }
+                    final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag);
+                    if (menuItem == null) {
+                        return;
+                    }
+                    mOnMenuItemClickListener.onMenuItemClick(menuItem);
+                }
+            };
+
+    private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
+    private boolean mIsOverflowOpen;
+
+    private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
+
+    private final Rect mPreviousContentRect = new Rect();
+    private int mSuggestedWidth;
+    private boolean mWidthChanged = true;
+
+    /**
+     * Initializes a new floating toolbar popup.
+     *
+     * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
+     *      from.
+     */
+    RemoteSelectionToolbar(Context context, View parent) {
+        mParent = Objects.requireNonNull(parent);
+        mContext = applyDefaultTheme(context);
+        mContentContainer = createContentContainer(mContext);
+        mPopupWindow = createPopupWindow(mContentContainer);
+        mMarginHorizontal = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        mMarginVertical = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
+        mLineHeight = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_height);
+        mIconTextSpacing = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
+
+        // Interpolators
+        mLogAccelerateInterpolator = new LogAccelerateInterpolator();
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.linear_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_linear_in);
+
+        // Drawables. Needed for views.
+        mArrow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
+        mArrow.setAutoMirrored(true);
+        mOverflow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
+        mOverflow.setAutoMirrored(true);
+        mToArrow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
+        mToArrow.setAutoMirrored(true);
+        mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
+        mToOverflow.setAutoMirrored(true);
+
+        // Views
+        mOverflowButton = createOverflowButton();
+        mOverflowButtonSize = measure(mOverflowButton);
+        mMainPanel = createMainPanel();
+        mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext, mIconTextSpacing);
+        mOverflowPanel = createOverflowPanel();
+
+        // Animation. Need views.
+        mOverflowAnimationListener = createOverflowAnimationListener();
+        mOpenOverflowAnimation = new AnimationSet(true);
+        mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mCloseOverflowAnimation = new AnimationSet(true);
+        mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mShowAnimation = createEnterAnimation(mContentContainer);
+        mDismissAnimation = createExitAnimation(
+                mContentContainer,
+                150,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                        mContentContainer.removeAllViews();
+                    }
+                });
+        mHideAnimation = createExitAnimation(
+                mContentContainer,
+                0,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                    }
+                });
+    }
+
+    @Override
+    public boolean setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        boolean ret = false;
+        if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
+            mPopupWindow.setOutsideTouchable(outsideTouchable);
+            mPopupWindow.setFocusable(!outsideTouchable);
+            mPopupWindow.update();
+            ret = true;
+        }
+        mPopupWindow.setOnDismissListener(onDismiss);
+        return ret;
+    }
+
+    /**
+     * Lays out buttons for the specified menu items.
+     * Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
+     */
+    private void layoutMenuItems(
+            List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener,
+            int suggestedWidth) {
+        cancelOverflowAnimations();
+        clearPanels();
+        updateMenuItems(menuItems, menuItemClickListener);
+        menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
+        if (!menuItems.isEmpty()) {
+            // Add remaining items to the overflow.
+            layoutOverflowPanelItems(menuItems);
+        }
+        updatePopupSize();
+    }
+
+    /**
+     * Updates the popup's menu items without rebuilding the widget.
+     * Use in place of layoutMenuItems() when the popup's views need not be reconstructed.
+     *
+     * @see #isLayoutRequired(List<MenuItem>)
+     */
+    private void updateMenuItems(
+            List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
+        mMenuItems.clear();
+        for (MenuItem menuItem : menuItems) {
+            mMenuItems.put(MenuItemRepr.of(menuItem), menuItem);
+        }
+        mOnMenuItemClickListener = menuItemClickListener;
+    }
+
+    /**
+     * Returns true if this popup needs a relayout to properly render the specified menu items.
+     */
+    private boolean isLayoutRequired(List<MenuItem> menuItems) {
+        return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
+    }
+
+    @Override
+    public void setWidthChanged(boolean widthChanged) {
+        mWidthChanged = widthChanged;
+    }
+
+    @Override
+    public void setSuggestedWidth(int suggestedWidth) {
+        // Check if there's been a substantial width spec change.
+        int difference = Math.abs(suggestedWidth - mSuggestedWidth);
+        mWidthChanged = difference > (mSuggestedWidth * 0.2);
+        mSuggestedWidth = suggestedWidth;
+    }
+
+    @Override
+    public void show(List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
+        if (isLayoutRequired(menuItems) || mWidthChanged) {
+            dismiss();
+            layoutMenuItems(menuItems, menuItemClickListener, mSuggestedWidth);
+        } else {
+            updateMenuItems(menuItems, menuItemClickListener);
+        }
+        if (!isShowing()) {
+            show(contentRect);
+        } else if (!mPreviousContentRect.equals(contentRect)) {
+            updateCoordinates(contentRect);
+        }
+        mWidthChanged = false;
+        mPreviousContentRect.set(contentRect);
+    }
+
+    private void show(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (isShowing()) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = false;
+        cancelDismissAndHideAnimations();
+        cancelOverflowAnimations();
+
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.showAtLocation(
+                mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
+        setTouchableSurfaceInsetsComputer();
+        runShowAnimation();
+    }
+
+    @Override
+    public void dismiss() {
+        if (mDismissed) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = true;
+        mHideAnimation.cancel();
+
+        runDismissAnimation();
+        setZeroTouchableSurface();
+    }
+
+    @Override
+    public void hide() {
+        if (!isShowing()) {
+            return;
+        }
+
+        mHidden = true;
+        runHideAnimation();
+        setZeroTouchableSurface();
+    }
+
+    @Override
+    public boolean isShowing() {
+        return !mDismissed && !mHidden;
+    }
+
+    @Override
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+    /**
+     * Updates the coordinates of this popup.
+     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+     * This is a no-op if this popup is not showing.
+     */
+    private void updateCoordinates(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (!isShowing() || !mPopupWindow.isShowing()) {
+            return;
+        }
+
+        cancelOverflowAnimations();
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.update(
+                mCoordsOnWindow.x, mCoordsOnWindow.y,
+                mPopupWindow.getWidth(), mPopupWindow.getHeight());
+    }
+
+    private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
+        refreshViewPort();
+
+        // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
+        // landscape.
+        final int x = Math.min(
+                contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+                mViewPortOnScreen.right - mPopupWindow.getWidth());
+
+        final int y;
+
+        final int availableHeightAboveContent =
+                contentRectOnScreen.top - mViewPortOnScreen.top;
+        final int availableHeightBelowContent =
+                mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
+
+        final int margin = 2 * mMarginVertical;
+        final int toolbarHeightWithVerticalMargin = mLineHeight + margin;
+
+        if (!hasOverflow()) {
+            if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the top of the content.
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the bottom of the content.
+                y = contentRectOnScreen.bottom;
+            } else if (availableHeightBelowContent >= mLineHeight) {
+                // Just enough space to fit the toolbar with no vertical margins.
+                y = contentRectOnScreen.bottom - mMarginVertical;
+            } else {
+                // Not enough space. Prefer to position as high as possible.
+                y = Math.max(
+                        mViewPortOnScreen.top,
+                        contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
+            }
+        } else {
+            // Has an overflow.
+            final int minimumOverflowHeightWithMargin =
+                    calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
+            final int availableHeightThroughContentDown =
+                    mViewPortOnScreen.bottom - contentRectOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+            final int availableHeightThroughContentUp =
+                    contentRectOnScreen.bottom - mViewPortOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+
+            if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the overflow.
+                // Position above and open upwards.
+                updateOverflowHeight(availableHeightAboveContent - margin);
+                y = contentRectOnScreen.top - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
+                    && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the main panel
+                // but not the overflow.
+                // Position above but open downwards.
+                updateOverflowHeight(availableHeightThroughContentDown - margin);
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the overflow.
+                // Position below and open downwards.
+                updateOverflowHeight(availableHeightBelowContent - margin);
+                y = contentRectOnScreen.bottom;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
+                    && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the main panel
+                // but not the overflow.
+                // Position below but open upwards.
+                updateOverflowHeight(availableHeightThroughContentUp - margin);
+                y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin
+                        - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else {
+                // Not enough space.
+                // Position at the top of the view port and open downwards.
+                updateOverflowHeight(mViewPortOnScreen.height() - margin);
+                y = mViewPortOnScreen.top;
+                mOpenOverflowUpwards = false;
+            }
+        }
+
+        // We later specify the location of PopupWindow relative to the attached window.
+        // The idea here is that 1) we can get the location of a View in both window coordinates
+        // and screen coordinates, where the offset between them should be equal to the window
+        // origin, and 2) we can use an arbitrary for this calculation while calculating the
+        // location of the rootview is supposed to be least expensive.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
+        // the following calculation.
+        mParent.getRootView().getLocationOnScreen(mTmpCoords);
+        int rootViewLeftOnScreen = mTmpCoords[0];
+        int rootViewTopOnScreen = mTmpCoords[1];
+        mParent.getRootView().getLocationInWindow(mTmpCoords);
+        int rootViewLeftOnWindow = mTmpCoords[0];
+        int rootViewTopOnWindow = mTmpCoords[1];
+        int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
+        int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
+        mCoordsOnWindow.set(
+                Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+    }
+
+    /**
+     * Performs the "show" animation on the floating popup.
+     */
+    private void runShowAnimation() {
+        mShowAnimation.start();
+    }
+
+    /**
+     * Performs the "dismiss" animation on the floating popup.
+     */
+    private void runDismissAnimation() {
+        mDismissAnimation.start();
+    }
+
+    /**
+     * Performs the "hide" animation on the floating popup.
+     */
+    private void runHideAnimation() {
+        mHideAnimation.start();
+    }
+
+    private void cancelDismissAndHideAnimations() {
+        mDismissAnimation.cancel();
+        mHideAnimation.cancel();
+    }
+
+    private void cancelOverflowAnimations() {
+        mContentContainer.clearAnimation();
+        mMainPanel.animate().cancel();
+        mOverflowPanel.animate().cancel();
+        mToArrow.stop();
+        mToOverflow.stop();
+    }
+
+    private void openOverflow() {
+        final int targetWidth = mOverflowPanelSize.getWidth();
+        final int targetHeight = mOverflowPanelSize.getHeight();
+        final int startWidth = mContentContainer.getWidth();
+        final int startHeight = mContentContainer.getHeight();
+        final float startY = mContentContainer.getY();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - startWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
+                }
+            }
+        };
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(
+                            startY - (mContentContainer.getHeight() - startHeight));
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX + targetWidth - mOverflowButton.getWidth()
+                        : overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mLogAccelerateInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.addAnimation(widthAnimation);
+        mOpenOverflowAnimation.addAnimation(heightAnimation);
+        mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mOpenOverflowAnimation);
+        mIsOverflowOpen = true;
+        mMainPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(250)
+                .start();
+        mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
+    }
+
+    private void closeOverflow() {
+        final int targetWidth = mMainPanelSize.getWidth();
+        final int startWidth = mContentContainer.getWidth();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
+                }
+            }
+        };
+        final int targetHeight = mMainPanelSize.getHeight();
+        final int startHeight = mContentContainer.getHeight();
+        final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(bottom - mContentContainer.getHeight());
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX - startWidth + mOverflowButton.getWidth()
+                        : overflowButtonStartX + startWidth - mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mLogAccelerateInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mCloseOverflowAnimation.getAnimations().clear();
+        mCloseOverflowAnimation.addAnimation(widthAnimation);
+        mCloseOverflowAnimation.addAnimation(heightAnimation);
+        mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mCloseOverflowAnimation);
+        mIsOverflowOpen = false;
+        mMainPanel.animate()
+                .alpha(1).withLayer()
+                .setInterpolator(mFastOutLinearInInterpolator)
+                .setDuration(100)
+                .start();
+        mOverflowPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(150)
+                .start();
+    }
+
+    /**
+     * Defines the position of the floating toolbar popup panels when transition animation has
+     * stopped.
+     */
+    private void setPanelsStatesAtRestingPosition() {
+        mOverflowButton.setEnabled(true);
+        mOverflowPanel.awakenScrollBars();
+
+        if (mIsOverflowOpen) {
+            // Set open state.
+            final Size containerSize = mOverflowPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(0);
+            mMainPanel.setVisibility(View.INVISIBLE);
+            mOverflowPanel.setAlpha(1);
+            mOverflowPanel.setVisibility(View.VISIBLE);
+            mOverflowButton.setImageDrawable(mArrow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_close_overflow_description));
+
+            // Update x-coordinates depending on RTL state.
+            if (isInRTLMode()) {
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mMainPanel.setX(0);  // align left
+                mOverflowButton.setX(// align right
+                        containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                mOverflowPanel.setX(0);  // align left
+            } else {
+                mContentContainer.setX(// align right
+                        mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                mMainPanel.setX(-mContentContainer.getX());  // align right
+                mOverflowButton.setX(0);  // align left
+                mOverflowPanel.setX(0);  // align left
+            }
+
+            // Update y-coordinates depending on overflow's open direction.
+            if (mOpenOverflowUpwards) {
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(// align bottom
+                        containerSize.getHeight() - mContentContainer.getHeight());
+                mOverflowButton.setY(// align bottom
+                        containerSize.getHeight() - mOverflowButtonSize.getHeight());
+                mOverflowPanel.setY(0);  // align top
+            } else {
+                // opens downwards.
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(0);  // align top
+                mOverflowButton.setY(0);  // align top
+                mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+            }
+        } else {
+            // Overflow not open. Set closed state.
+            final Size containerSize = mMainPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(1);
+            mMainPanel.setVisibility(View.VISIBLE);
+            mOverflowPanel.setAlpha(0);
+            mOverflowPanel.setVisibility(View.INVISIBLE);
+            mOverflowButton.setImageDrawable(mOverflow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_open_overflow_description));
+
+            if (hasOverflow()) {
+                // Update x-coordinates depending on RTL state.
+                if (isInRTLMode()) {
+                    mContentContainer.setX(mMarginHorizontal);  // align left
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(0);  // align left
+                    mOverflowPanel.setX(0);  // align left
+                } else {
+                    mContentContainer.setX(// align right
+                            mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(// align right
+                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                    mOverflowPanel.setX(// align right
+                            containerSize.getWidth() - mOverflowPanelSize.getWidth());
+                }
+
+                // Update y-coordinates depending on overflow's open direction.
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(// align bottom
+                            mMarginVertical + mOverflowPanelSize.getHeight()
+                                    - containerSize.getHeight());
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(// align bottom
+                            containerSize.getHeight() - mOverflowPanelSize.getHeight());
+                } else {
+                    // opens downwards.
+                    mContentContainer.setY(mMarginVertical);  // align top
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+                }
+            } else {
+                // No overflow.
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setX(0);  // align left
+                mMainPanel.setY(0);  // align top
+            }
+        }
+    }
+
+    private void updateOverflowHeight(int suggestedHeight) {
+        if (hasOverflow()) {
+            final int maxItemSize =
+                    (suggestedHeight - mOverflowButtonSize.getHeight()) / mLineHeight;
+            final int newHeight = calculateOverflowHeight(maxItemSize);
+            if (mOverflowPanelSize.getHeight() != newHeight) {
+                mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
+            }
+            setSize(mOverflowPanel, mOverflowPanelSize);
+            if (mIsOverflowOpen) {
+                setSize(mContentContainer, mOverflowPanelSize);
+                if (mOpenOverflowUpwards) {
+                    final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
+                    mContentContainer.setY(mContentContainer.getY() + deltaHeight);
+                    mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
+                }
+            } else {
+                setSize(mContentContainer, mMainPanelSize);
+            }
+            updatePopupSize();
+        }
+    }
+
+    private void updatePopupSize() {
+        int width = 0;
+        int height = 0;
+        if (mMainPanelSize != null) {
+            width = Math.max(width, mMainPanelSize.getWidth());
+            height = Math.max(height, mMainPanelSize.getHeight());
+        }
+        if (mOverflowPanelSize != null) {
+            width = Math.max(width, mOverflowPanelSize.getWidth());
+            height = Math.max(height, mOverflowPanelSize.getHeight());
+        }
+        mPopupWindow.setWidth(width + mMarginHorizontal * 2);
+        mPopupWindow.setHeight(height + mMarginVertical * 2);
+        maybeComputeTransitionDurationScale();
+    }
+
+    private void refreshViewPort() {
+        mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
+    }
+
+    private int getAdjustedToolbarWidth(int suggestedWidth) {
+        int width = suggestedWidth;
+        refreshViewPort();
+        int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        if (width <= 0) {
+            width = mParent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+        }
+        return Math.min(width, maximumWidth);
+    }
+
+    /**
+     * Sets the touchable region of this popup to be zero. This means that all touch events on
+     * this popup will go through to the surface behind it.
+     */
+    private void setZeroTouchableSurface() {
+        mTouchableRegion.setEmpty();
+    }
+
+    /**
+     * Sets the touchable region of this popup to be the area occupied by its content.
+     */
+    private void setContentAreaAsTouchableSurface() {
+        Objects.requireNonNull(mMainPanelSize);
+        final int width;
+        final int height;
+        if (mIsOverflowOpen) {
+            Objects.requireNonNull(mOverflowPanelSize);
+            width = mOverflowPanelSize.getWidth();
+            height = mOverflowPanelSize.getHeight();
+        } else {
+            width = mMainPanelSize.getWidth();
+            height = mMainPanelSize.getHeight();
+        }
+        mTouchableRegion.set(
+                (int) mContentContainer.getX(),
+                (int) mContentContainer.getY(),
+                (int) mContentContainer.getX() + width,
+                (int) mContentContainer.getY() + height);
+    }
+
+    /**
+     * Make the touchable area of this popup be the area specified by mTouchableRegion.
+     * This should be called after the popup window has been dismissed (dismiss/hide)
+     * and is probably being re-shown with a new content root view.
+     */
+    private void setTouchableSurfaceInsetsComputer() {
+        ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
+                .getRootView()
+                .getViewTreeObserver();
+        viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
+        viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
+    }
+
+    private boolean isInRTLMode() {
+        return mContext.getApplicationInfo().hasRtlSupport()
+                && mContext.getResources().getConfiguration().getLayoutDirection()
+                == View.LAYOUT_DIRECTION_RTL;
+    }
+
+    private boolean hasOverflow() {
+        return mOverflowPanelSize != null;
+    }
+
+    /**
+     * Fits as many menu items in the main panel and returns a list of the menu items that
+     * were not fit in.
+     *
+     * @return The menu items that are not included in this main panel.
+     */
+    public List<MenuItem> layoutMainPanelItems(
+            List<MenuItem> menuItems, final int toolbarWidth) {
+        Objects.requireNonNull(menuItems);
+
+        int availableWidth = toolbarWidth;
+
+        final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
+        // add the overflow menu items to the end of the remainingMenuItems list.
+        final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
+        for (MenuItem menuItem : menuItems) {
+            if (menuItem.getItemId() != android.R.id.textAssist
+                    && menuItem.requiresOverflow()) {
+                overflowMenuItems.add(menuItem);
+            } else {
+                remainingMenuItems.add(menuItem);
+            }
+        }
+        remainingMenuItems.addAll(overflowMenuItems);
+
+        mMainPanel.removeAllViews();
+        mMainPanel.setPaddingRelative(0, 0, 0, 0);
+
+        int lastGroupId = -1;
+        boolean isFirstItem = true;
+        while (!remainingMenuItems.isEmpty()) {
+            final MenuItem menuItem = remainingMenuItems.peek();
+
+            // if this is the first item, regardless of requiresOverflow(), it should be
+            // displayed on the main panel. Otherwise all items including this one will be
+            // overflow items, and should be displayed in overflow panel.
+            if (!isFirstItem && menuItem.requiresOverflow()) {
+                break;
+            }
+
+            final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
+            final View menuItemButton = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, showIcon);
+            if (!showIcon && menuItemButton instanceof LinearLayout) {
+                ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+            }
+
+            // Adding additional start padding for the first button to even out button spacing.
+            if (isFirstItem) {
+                menuItemButton.setPaddingRelative(
+                        (int) (1.5 * menuItemButton.getPaddingStart()),
+                        menuItemButton.getPaddingTop(),
+                        menuItemButton.getPaddingEnd(),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            // Adding additional end padding for the last button to even out button spacing.
+            boolean isLastItem = remainingMenuItems.size() == 1;
+            if (isLastItem) {
+                menuItemButton.setPaddingRelative(
+                        menuItemButton.getPaddingStart(),
+                        menuItemButton.getPaddingTop(),
+                        (int) (1.5 * menuItemButton.getPaddingEnd()),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            final int menuItemButtonWidth = Math.min(
+                    menuItemButton.getMeasuredWidth(), toolbarWidth);
+
+            // Check if we can fit an item while reserving space for the overflowButton.
+            final boolean canFitWithOverflow =
+                    menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth();
+            final boolean canFitNoOverflow =
+                    isLastItem && menuItemButtonWidth <= availableWidth;
+            if (canFitWithOverflow || canFitNoOverflow) {
+                setButtonTagAndClickListener(menuItemButton, menuItem);
+                // Set tooltips for main panel items, but not overflow items (b/35726766).
+                menuItemButton.setTooltipText(menuItem.getTooltipText());
+                mMainPanel.addView(menuItemButton);
+                final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
+                params.width = menuItemButtonWidth;
+                menuItemButton.setLayoutParams(params);
+                availableWidth -= menuItemButtonWidth;
+                remainingMenuItems.pop();
+            } else {
+                break;
+            }
+            lastGroupId = menuItem.getGroupId();
+            isFirstItem = false;
+        }
+
+        if (!remainingMenuItems.isEmpty()) {
+            // Reserve space for overflowButton.
+            mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
+        }
+
+        mMainPanelSize = measure(mMainPanel);
+        return remainingMenuItems;
+    }
+
+    private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        final int size = menuItems.size();
+        for (int i = 0; i < size; i++) {
+            overflowPanelAdapter.add(menuItems.get(i));
+        }
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        if (mOpenOverflowUpwards) {
+            mOverflowPanel.setY(0);
+        } else {
+            mOverflowPanel.setY(mOverflowButtonSize.getHeight());
+        }
+
+        int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
+        int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
+        mOverflowPanelSize = new Size(width, height);
+        setSize(mOverflowPanel, mOverflowPanelSize);
+    }
+
+    /**
+     * Resets the content container and appropriately position it's panels.
+     */
+    private void preparePopupContent() {
+        mContentContainer.removeAllViews();
+
+        // Add views in the specified order so they stack up as expected.
+        // Order: overflowPanel, mainPanel, overflowButton.
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowPanel);
+        }
+        mContentContainer.addView(mMainPanel);
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowButton);
+        }
+        setPanelsStatesAtRestingPosition();
+        setContentAreaAsTouchableSurface();
+
+        // The positioning of contents in RTL is wrong when the view is first rendered.
+        // Hide the view and post a runnable to recalculate positions and render the view.
+        // TODO: Investigate why this happens and fix.
+        if (isInRTLMode()) {
+            mContentContainer.setAlpha(0);
+            mContentContainer.post(mPreparePopupContentRTLHelper);
+        }
+    }
+
+    /**
+     * Clears out the panels and their container. Resets their calculated sizes.
+     */
+    private void clearPanels() {
+        mOverflowPanelSize = null;
+        mMainPanelSize = null;
+        mIsOverflowOpen = false;
+        mMainPanel.removeAllViews();
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        mContentContainer.removeAllViews();
+    }
+
+    private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
+        if (mOpenOverflowUpwards) {
+            mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
+            mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
+            mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
+        }
+    }
+
+    private int getOverflowWidth() {
+        int overflowWidth = 0;
+        final int count = mOverflowPanel.getAdapter().getCount();
+        for (int i = 0; i < count; i++) {
+            MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
+            overflowWidth =
+                    Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
+        }
+        return overflowWidth;
+    }
+
+    private int calculateOverflowHeight(int maxItemSize) {
+        // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
+        int actualSize = Math.min(
+                MAX_OVERFLOW_SIZE,
+                Math.min(
+                        Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
+                        mOverflowPanel.getCount()));
+        int extension = 0;
+        if (actualSize < mOverflowPanel.getCount()) {
+            // The overflow will require scrolling to get to all the items.
+            // Extend the height so that part of the hidden items is displayed.
+            extension = (int) (mLineHeight * 0.5f);
+        }
+        return actualSize * mLineHeight
+                + mOverflowButtonSize.getHeight()
+                + extension;
+    }
+
+    private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
+        menuItemButton.setTag(MenuItemRepr.of(menuItem));
+        menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+    }
+
+    /**
+     * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
+     * animations. See comment about this in the code.
+     */
+    private int getAdjustedDuration(int originalDuration) {
+        if (mTransitionDurationScale < 150) {
+            // For smaller transition, decrease the time.
+            return Math.max(originalDuration - 50, 0);
+        } else if (mTransitionDurationScale > 300) {
+            // For bigger transition, increase the time.
+            return originalDuration + 50;
+        }
+
+        // Scale the animation duration with getDurationScale(). This allows
+        // android.view.animation.* animations to scale just like android.animation.* animations
+        // when  animator duration scale is adjusted in "Developer Options".
+        // For this reason, do not use this method for android.animation.* animations.
+        return (int) (originalDuration * ValueAnimator.getDurationScale());
+    }
+
+    private void maybeComputeTransitionDurationScale() {
+        if (mMainPanelSize != null && mOverflowPanelSize != null) {
+            int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
+            int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
+            mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h)
+                    / mContentContainer.getContext().getResources().getDisplayMetrics().density);
+        }
+    }
+
+    private ViewGroup createMainPanel() {
+        ViewGroup mainPanel = new LinearLayout(mContext) {
+            @Override
+            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                if (isOverflowAnimating()) {
+                    // Update widthMeasureSpec to make sure that this view is not clipped
+                    // as we offset its coordinates with respect to its parent.
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            mMainPanelSize.getWidth(),
+                            MeasureSpec.EXACTLY);
+                }
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+
+            @Override
+            public boolean onInterceptTouchEvent(MotionEvent ev) {
+                // Intercept the touch event while the overflow is animating.
+                return isOverflowAnimating();
+            }
+        };
+        return mainPanel;
+    }
+
+    private ImageButton createOverflowButton() {
+        final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
+                .inflate(R.layout.floating_popup_overflow_button, null);
+        overflowButton.setImageDrawable(mOverflow);
+        overflowButton.setOnClickListener(v -> {
+            if (mIsOverflowOpen) {
+                overflowButton.setImageDrawable(mToOverflow);
+                mToOverflow.start();
+                closeOverflow();
+            } else {
+                overflowButton.setImageDrawable(mToArrow);
+                mToArrow.start();
+                openOverflow();
+            }
+        });
+        return overflowButton;
+    }
+
+    private OverflowPanel createOverflowPanel() {
+        final OverflowPanel overflowPanel = new OverflowPanel(this);
+        overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        overflowPanel.setDivider(null);
+        overflowPanel.setDividerHeight(0);
+
+        final ArrayAdapter adapter =
+                new ArrayAdapter<MenuItem>(mContext, 0) {
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        return mOverflowPanelViewHelper.getView(
+                                getItem(position), mOverflowPanelSize.getWidth(), convertView);
+                    }
+                };
+        overflowPanel.setAdapter(adapter);
+
+        overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
+            MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
+            if (mOnMenuItemClickListener != null) {
+                mOnMenuItemClickListener.onMenuItemClick(menuItem);
+            }
+        });
+
+        return overflowPanel;
+    }
+
+    private boolean isOverflowAnimating() {
+        final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
+                && !mOpenOverflowAnimation.hasEnded();
+        final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
+                && !mCloseOverflowAnimation.hasEnded();
+        return overflowOpening || overflowClosing;
+    }
+
+    private Animation.AnimationListener createOverflowAnimationListener() {
+        Animation.AnimationListener listener = new Animation.AnimationListener() {
+            @Override
+            public void onAnimationStart(Animation animation) {
+                // Disable the overflow button while it's animating.
+                // It will be re-enabled when the animation stops.
+                mOverflowButton.setEnabled(false);
+                // Ensure both panels have visibility turned on when the overflow animation
+                // starts.
+                mMainPanel.setVisibility(View.VISIBLE);
+                mOverflowPanel.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                // Posting this because it seems like this is called before the animation
+                // actually ends.
+                mContentContainer.post(() -> {
+                    setPanelsStatesAtRestingPosition();
+                    setContentAreaAsTouchableSurface();
+                });
+            }
+
+            @Override
+            public void onAnimationRepeat(Animation animation) {
+            }
+        };
+        return listener;
+    }
+
+    private static Size measure(View view) {
+        Preconditions.checkState(view.getParent() == null);
+        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
+    }
+
+    private static void setSize(View view, int width, int height) {
+        view.setMinimumWidth(width);
+        view.setMinimumHeight(height);
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
+        params.width = width;
+        params.height = height;
+        view.setLayoutParams(params);
+    }
+
+    private static void setSize(View view, Size size) {
+        setSize(view, size.getWidth(), size.getHeight());
+    }
+
+    private static void setWidth(View view, int width) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, width, params.height);
+    }
+
+    private static void setHeight(View view, int height) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, params.width, height);
+    }
+
+    /**
+     * A custom ListView for the overflow panel.
+     */
+    private static final class OverflowPanel extends ListView {
+
+        private final RemoteSelectionToolbar mPopup;
+
+        OverflowPanel(RemoteSelectionToolbar popup) {
+            super(Objects.requireNonNull(popup).mContext);
+            this.mPopup = popup;
+            setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+            setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            // Update heightMeasureSpec to make sure that this view is not clipped
+            // as we offset it's coordinates with respect to its parent.
+            int height = mPopup.mOverflowPanelSize.getHeight()
+                    - mPopup.mOverflowButtonSize.getHeight();
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            if (mPopup.isOverflowAnimating()) {
+                // Eat the touch event.
+                return true;
+            }
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected boolean awakenScrollBars() {
+            return super.awakenScrollBars();
+        }
+    }
+
+    /**
+     * A custom interpolator used for various floating toolbar animations.
+     */
+    private static final class LogAccelerateInterpolator implements Interpolator {
+
+        private static final int BASE = 100;
+        private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
+
+        private static float computeLog(float t, int base) {
+            return (float) (1 - Math.pow(base, -t));
+        }
+
+        @Override
+        public float getInterpolation(float t) {
+            return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
+        }
+    }
+
+    /**
+     * A helper for generating views for the overflow panel.
+     */
+    private static final class OverflowPanelViewHelper {
+
+        private final View mCalculator;
+        private final int mIconTextSpacing;
+        private final int mSidePadding;
+
+        private final Context mContext;
+
+        OverflowPanelViewHelper(Context context, int iconTextSpacing) {
+            mContext = Objects.requireNonNull(context);
+            mIconTextSpacing = iconTextSpacing;
+            mSidePadding = context.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
+            mCalculator = createMenuButton(null);
+        }
+
+        public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
+            Objects.requireNonNull(menuItem);
+            if (convertView != null) {
+                updateMenuItemButton(
+                        convertView, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            } else {
+                convertView = createMenuButton(menuItem);
+            }
+            convertView.setMinimumWidth(minimumWidth);
+            return convertView;
+        }
+
+        public int calculateWidth(MenuItem menuItem) {
+            updateMenuItemButton(
+                    mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            mCalculator.measure(
+                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+            return mCalculator.getMeasuredWidth();
+        }
+
+        private View createMenuButton(MenuItem menuItem) {
+            View button = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            button.setPadding(mSidePadding, 0, mSidePadding, 0);
+            return button;
+        }
+
+        private boolean shouldShowIcon(MenuItem menuItem) {
+            if (menuItem != null) {
+                return menuItem.getGroupId() == android.R.id.textAssist;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Creates and returns a menu button for the specified menu item.
+     */
+    private static View createMenuItemButton(
+            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final View menuItemButton = LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_menu_button, null);
+        if (menuItem != null) {
+            updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
+        }
+        return menuItemButton;
+    }
+
+    /**
+     * Updates the specified menu item button with the specified menu item data.
+     */
+    private static void updateMenuItemButton(
+            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final TextView buttonText = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_text);
+        buttonText.setEllipsize(null);
+        if (TextUtils.isEmpty(menuItem.getTitle())) {
+            buttonText.setVisibility(View.GONE);
+        } else {
+            buttonText.setVisibility(View.VISIBLE);
+            buttonText.setText(menuItem.getTitle());
+        }
+        final ImageView buttonIcon = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_image);
+        if (menuItem.getIcon() == null || !showIcon) {
+            buttonIcon.setVisibility(View.GONE);
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(0, 0, 0, 0);
+            }
+        } else {
+            buttonIcon.setVisibility(View.VISIBLE);
+            buttonIcon.setImageDrawable(menuItem.getIcon());
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
+            }
+        }
+        final CharSequence contentDescription = menuItem.getContentDescription();
+        if (TextUtils.isEmpty(contentDescription)) {
+            menuItemButton.setContentDescription(menuItem.getTitle());
+        } else {
+            menuItemButton.setContentDescription(contentDescription);
+        }
+    }
+
+    private static ViewGroup createContentContainer(Context context) {
+        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_container, null);
+        contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        contentContainer.setTag("floating_toolbar");
+        contentContainer.setClipToOutline(true);
+        return contentContainer;
+    }
+
+    private static PopupWindow createPopupWindow(ViewGroup content) {
+        ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+        // TODO: Use .setIsLaidOutInScreen(true) instead of .setClippingEnabled(false)
+        // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
+        popupWindow.setClippingEnabled(false);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+        popupWindow.setAnimationStyle(0);
+        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        content.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        popupContentHolder.addView(content);
+        return popupWindow;
+    }
+
+    /**
+     * Creates an "appear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     */
+    private static AnimatorSet createEnterAnimation(View view) {
+        AnimatorSet animation = new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
+        return animation;
+    }
+
+    /**
+     * Creates a "disappear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     * @param startDelay  The start delay of the animation
+     * @param listener  The animation listener
+     */
+    private static AnimatorSet createExitAnimation(
+            View view, int startDelay, Animator.AnimatorListener listener) {
+        AnimatorSet animation =  new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
+        animation.setStartDelay(startDelay);
+        animation.addListener(listener);
+        return animation;
+    }
+
+    /**
+     * Returns a re-themed context with controlled look and feel for views.
+     */
+    private static Context applyDefaultTheme(Context originalContext) {
+        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
+        boolean isLightTheme = a.getBoolean(0, true);
+        int themeId =
+                isLightTheme ? R.style.Theme_DeviceDefault_Light : R.style.Theme_DeviceDefault;
+        a.recycle();
+        return new ContextThemeWrapper(originalContext, themeId);
+    }
+
+    /**
+     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
+     */
+    @VisibleForTesting
+    public static final class MenuItemRepr {
+
+        public final int itemId;
+        public final int groupId;
+        @Nullable public final String title;
+        @Nullable private final Drawable mIcon;
+
+        private MenuItemRepr(
+                int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) {
+            this.itemId = itemId;
+            this.groupId = groupId;
+            this.title = (title == null) ? null : title.toString();
+            mIcon = icon;
+        }
+
+        /**
+         * Creates an instance of MenuItemRepr for the specified menu item.
+         */
+        public static MenuItemRepr of(MenuItem menuItem) {
+            return new MenuItemRepr(
+                    menuItem.getItemId(),
+                    menuItem.getGroupId(),
+                    menuItem.getTitle(),
+                    menuItem.getIcon());
+        }
+
+        /**
+         * Returns this object's hashcode.
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(itemId, groupId, title, mIcon);
+        }
+
+        /**
+         * Returns true if this object is the same as the specified object.
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof MenuItemRepr)) {
+                return false;
+            }
+            final MenuItemRepr other = (MenuItemRepr) o;
+            return itemId == other.itemId
+                    && groupId == other.groupId
+                    && TextUtils.equals(title, other.title)
+                    // Many Drawables (icons) do not implement equals(). Using equals() here instead
+                    // of reference comparisons in case a Drawable subclass implements equals().
+                    && Objects.equals(mIcon, other.mIcon);
+        }
+
+        /**
+         * Returns true if the two menu item collections are the same based on MenuItemRepr.
+         */
+        public static boolean reprEquals(
+                Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
+            if (menuItems1.size() != menuItems2.size()) {
+                return false;
+            }
+
+            final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
+            for (MenuItem menuItem1 : menuItems1) {
+                final MenuItem menuItem2 = menuItems2Iter.next();
+                if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd4355d..f2a0355 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -87,6 +87,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.view.WindowLayout;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.window.ClientWindowFrames;
@@ -389,8 +390,9 @@
             public void resized(ClientWindowFrames frames, boolean reportDraw,
                     MergedConfiguration mergedConfiguration, boolean forceLayout,
                     boolean alwaysConsumeSystemBars, int displayId) {
-                Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
-                        reportDraw ? 1 : 0);
+                Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
+                        reportDraw ? 1 : 0,
+                        mergedConfiguration);
                 mCaller.sendMessage(msg);
             }
 
@@ -1028,6 +1030,10 @@
             }
         }
 
+        private void updateConfiguration(MergedConfiguration mergedConfiguration) {
+            mMergedConfiguration.setTo(mergedConfiguration);
+        }
+
         void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
             if (mDestroyed) {
                 Log.w(TAG, "Ignoring updateSurface due to destroyed");
@@ -1066,8 +1072,6 @@
                     mLayout.x = 0;
                     mLayout.y = 0;
 
-                    mLayout.width = myWidth;
-                    mLayout.height = myHeight;
                     mLayout.format = mFormat;
 
                     mCurWindowFlags = mWindowFlags;
@@ -1076,6 +1080,23 @@
                             | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                             | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+
+                    final Configuration config = mMergedConfiguration.getMergedConfiguration();
+                    final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+                    if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
+                            && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+                        mLayout.width = myWidth;
+                        mLayout.height = myHeight;
+                        mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SCALED;
+                    } else {
+                        final float layoutScale = Math.max(
+                                maxBounds.width() / (float) myWidth,
+                                maxBounds.height() / (float) myHeight);
+                        mLayout.width = (int) (myWidth * layoutScale + .5f);
+                        mLayout.height = (int) (myHeight * layoutScale + .5f);
+                        mLayout.flags |= WindowManager.LayoutParams.FLAG_SCALED;
+                    }
+
                     mCurWindowPrivateFlags = mWindowPrivateFlags;
                     mLayout.privateFlags = mWindowPrivateFlags;
 
@@ -1121,12 +1142,14 @@
 
                     final int relayoutResult = mSession.relayout(
                             mWindow, mLayout, mWidth, mHeight,
-                            View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
-                            mInsetsState, mTempControls, mSurfaceSize);
+                            View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl,
+                            mInsetsState, mTempControls);
 
                     final int transformHint = SurfaceControl.rotationToBufferTransform(
                             (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
                     mSurfaceControl.setTransformHint(transformHint);
+                    WindowLayout.computeSurfaceSize(mLayout, maxBounds, mWidth, mHeight,
+                            mWinFrames.frame, false /* dragResizing */, mSurfaceSize);
 
                     if (mSurfaceControl.isValid()) {
                         if (mBbqSurfaceControl == null) {
@@ -1164,7 +1187,6 @@
                     int h = mWinFrames.frame.height();
 
                     final DisplayCutout rawCutout = mInsetsState.getDisplayCutout();
-                    final Configuration config = getResources().getConfiguration();
                     final Rect visibleFrame = new Rect(mWinFrames.frame);
                     visibleFrame.intersect(mInsetsState.getDisplayFrame());
                     WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
@@ -2321,6 +2343,7 @@
                 } break;
                 case MSG_WINDOW_RESIZED: {
                     final boolean reportDraw = message.arg1 != 0;
+                    mEngine.updateConfiguration(((MergedConfiguration) message.obj));
                     mEngine.updateSurface(true, false, reportDraw);
                     mEngine.doOffsetsChanged(true);
                     mEngine.scaleAndCropScreenshot();
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 69af2a5..bd468d9 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -17,7 +17,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UiThread;
+import android.graphics.Region;
 import android.hardware.HardwareBuffer;
 
 /**
@@ -124,4 +126,16 @@
     default void removeOnBufferTransformHintChangedListener(
             @NonNull OnBufferTransformHintChangedListener listener) {
     }
+
+    /**
+     * Sets the touchable region for this SurfaceControl, expressed in surface local
+     * coordinates. By default the touchable region is the entire Layer, indicating
+     * that if the layer is otherwise eligible to receive touch it receives touch
+     * on the entire surface. Setting the touchable region allows the SurfaceControl
+     * to receive touch in some regions, while allowing for pass-through in others.
+     *
+     * @param r The region to use or null to use the entire Layer bounds
+     */
+    default void setTouchableRegion(@Nullable Region r) {
+    }
 }
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 9889eaa..9b36b9b 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -53,6 +53,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -386,6 +387,33 @@
      * @param boundBottom the bottom bounding rect of the display cutout in pixels.  If null is
      *                   passed, it's treated as an empty rectangle (0,0)-(0,0).
      * @param waterfallInsets the insets for the curved areas in waterfall display.
+     * @param info the cutout path parser info.
+     * @hide
+     */
+    public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
+            @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
+            @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info) {
+        this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
+                info, true);
+    }
+
+    /**
+     * Creates a DisplayCutout instance.
+     *
+     * <p>Note that this is only useful for tests. For production code, developers should always
+     * use a {@link DisplayCutout} obtained from the system.</p>
+     *
+     * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+     *                   {@link #getSafeInsetTop()} etc.
+     * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundTop the top bounding rect of the display cutout in pixels.  If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundRight the right bounding rect of the display cutout in pixels.  If null is
+     *                  passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundBottom the bottom bounding rect of the display cutout in pixels.  If null is
+     *                   passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param waterfallInsets the insets for the curved areas in waterfall display.
      */
     public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
@@ -1098,6 +1126,85 @@
     }
 
     /**
+     * @return a copy of this cutout that has been rotated for a display in toRotation.
+     * @hide
+     */
+    public DisplayCutout getRotated(int startWidth, int startHeight,
+            int fromRotation, int toRotation) {
+        if (this == DisplayCutout.NO_CUTOUT) {
+            return DisplayCutout.NO_CUTOUT;
+        }
+        final int rotation = RotationUtils.deltaRotation(fromRotation, toRotation);
+        if (rotation == ROTATION_0) {
+            return this;
+        }
+        final Insets waterfallInsets = RotationUtils.rotateInsets(getWaterfallInsets(), rotation);
+        // returns a copy
+        final Rect[] newBounds = getBoundingRectsAll();
+        final Rect displayBounds = new Rect(0, 0, startWidth, startHeight);
+        for (int i = 0; i < newBounds.length; ++i) {
+            if (newBounds[i].isEmpty()) continue;
+            RotationUtils.rotateBounds(newBounds[i], displayBounds, rotation);
+        }
+        Collections.rotate(Arrays.asList(newBounds), -rotation);
+        final CutoutPathParserInfo info = getCutoutPathParserInfo();
+        final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
+                info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+                info.getCutoutSpec(), toRotation, info.getScale());
+        final boolean swapAspect = (rotation % 2) != 0;
+        final int endWidth = swapAspect ? startHeight : startWidth;
+        final int endHeight = swapAspect ? startWidth : startHeight;
+        final DisplayCutout tmp =
+                DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo);
+        final Rect safeInsets = DisplayCutout.computeSafeInsets(endWidth, endHeight, tmp);
+        return tmp.replaceSafeInsets(safeInsets);
+    }
+
+    /**
+     * Compute the insets derived from a cutout. This is usually used to populate the safe-insets
+     * of the cutout via {@link #replaceSafeInsets}.
+     * @hide
+     */
+    public static Rect computeSafeInsets(int displayW, int displayH, DisplayCutout cutout) {
+        if (displayW == displayH) {
+            throw new UnsupportedOperationException("not implemented: display=" + displayW + "x"
+                    + displayH + " cutout=" + cutout);
+        }
+
+        int leftInset = Math.max(cutout.getWaterfallInsets().left, findCutoutInsetForSide(
+                displayW, displayH, cutout.getBoundingRectLeft(), Gravity.LEFT));
+        int topInset = Math.max(cutout.getWaterfallInsets().top, findCutoutInsetForSide(
+                displayW, displayH, cutout.getBoundingRectTop(), Gravity.TOP));
+        int rightInset = Math.max(cutout.getWaterfallInsets().right, findCutoutInsetForSide(
+                displayW, displayH, cutout.getBoundingRectRight(), Gravity.RIGHT));
+        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, findCutoutInsetForSide(
+                displayW, displayH, cutout.getBoundingRectBottom(), Gravity.BOTTOM));
+
+        return new Rect(leftInset, topInset, rightInset, bottomInset);
+    }
+
+    private static int findCutoutInsetForSide(int displayW, int displayH, Rect boundingRect,
+            int gravity) {
+        if (boundingRect.isEmpty()) {
+            return 0;
+        }
+
+        int inset = 0;
+        switch (gravity) {
+            case Gravity.TOP:
+                return Math.max(inset, boundingRect.bottom);
+            case Gravity.BOTTOM:
+                return Math.max(inset, displayH - boundingRect.top);
+            case Gravity.LEFT:
+                return Math.max(inset, boundingRect.right);
+            case Gravity.RIGHT:
+                return Math.max(inset, displayW - boundingRect.left);
+            default:
+                throw new IllegalArgumentException("unknown gravity: " + gravity);
+        }
+    }
+
+    /**
      * Helper class for passing {@link DisplayCutout} through binder.
      *
      * Needed, because {@code readFromParcel} cannot be used with immutable classes.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 6226566..ccf1e44 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -74,7 +74,6 @@
      * @param viewVisibility Window root view's visibility.
      * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING},
      * {@link WindowManagerGlobal#RELAYOUT_DEFER_SURFACE_DESTROY}.
-     * @param frameNumber A frame number in which changes requested in this layout will be rendered.
      * @param outFrame Rect in which is placed the new position/size on
      * screen.
      * @param outContentInsets Rect in which is placed the offsets from
@@ -97,17 +96,15 @@
      * since it was last displayed.
      * @param outSurface Object in which is placed the new display surface.
      * @param insetsState The current insets state in the system.
-     * @param outSurfaceSize The width and height of the surface control
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
      * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
      */
     int relayout(IWindow window, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
-            int flags, long frameNumber, out ClientWindowFrames outFrames,
+            int flags, out ClientWindowFrames outFrames,
             out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
-            out InsetsState insetsState, out InsetsSourceControl[] activeControls,
-            out Point outSurfaceSize);
+            out InsetsState insetsState, out InsetsSourceControl[] activeControls);
 
     /*
      * Notify the window manager that an application is relaunching and
@@ -342,4 +339,9 @@
      * @param callback The {@link IOnBackInvokedCallback} to set.
      */
     oneway void setOnBackInvokedCallback(IWindow window, IOnBackInvokedCallback callback);
+
+    /**
+     * Clears a touchable region set by {@link #setInsets}.
+     */
+    void clearTouchableRegion(IWindow window);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 97b5a31..1496a4a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -52,6 +52,7 @@
 import static android.view.ViewRootImplProto.WIDTH;
 import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
 import static android.view.ViewRootImplProto.WIN_FRAME;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
 import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -83,6 +84,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
@@ -471,6 +474,9 @@
     final Region mTransparentRegion;
     final Region mPreviousTransparentRegion;
 
+    Region mTouchableRegion;
+    Region mPreviousTouchableRegion;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     int mWidth;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2868,9 +2874,9 @@
                 }
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                 final boolean freeformResizing = (relayoutResult
-                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
+                        & RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
                 final boolean dockedResizing = (relayoutResult
-                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
+                        & RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
                 final boolean dragResizing = freeformResizing || dockedResizing;
                 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC) != 0) {
                     if (DEBUG_BLAST) {
@@ -3249,9 +3255,15 @@
             mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
         }
 
+        Rect contentInsets = null;
+        Rect visibleInsets = null;
+        Region touchableRegion = null;
+        int touchableInsetMode = TOUCHABLE_INSETS_REGION;
+        boolean computedInternalInsets = false;
         if (computesInternalInsets) {
-            // Clear the original insets.
             final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
+
+            // Clear the original insets.
             insets.reset();
 
             // Compute new insets in place.
@@ -3263,9 +3275,6 @@
                 mLastGivenInsets.set(insets);
 
                 // Translate insets to screen coordinates if needed.
-                final Rect contentInsets;
-                final Rect visibleInsets;
-                final Region touchableRegion;
                 if (mTranslator != null) {
                     contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
                     visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
@@ -3275,12 +3284,46 @@
                     visibleInsets = insets.visibleInsets;
                     touchableRegion = insets.touchableRegion;
                 }
-
-                try {
-                    mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
-                            contentInsets, visibleInsets, touchableRegion);
-                } catch (RemoteException e) {
+                computedInternalInsets = true;
+            }
+            touchableInsetMode = insets.mTouchableInsets;
+        }
+        boolean needsSetInsets = computedInternalInsets;
+        needsSetInsets |= !Objects.equals(mPreviousTouchableRegion, mTouchableRegion) &&
+            (mTouchableRegion != null);
+        if (needsSetInsets) {
+            if (mTouchableRegion != null) {
+                if (mPreviousTouchableRegion == null) {
+                    mPreviousTouchableRegion = new Region();
                 }
+                mPreviousTouchableRegion.set(mTouchableRegion);
+                if (touchableInsetMode != TOUCHABLE_INSETS_REGION) {
+                    Log.e(mTag, "Setting touchableInsetMode to non TOUCHABLE_INSETS_REGION" +
+                          " from OnComputeInternalInsets, while also using setTouchableRegion" +
+                          " causes setTouchableRegion to be ignored");
+                }
+            } else {
+                mPreviousTouchableRegion = null;
+            }
+            if (contentInsets == null) contentInsets = new Rect(0,0,0,0);
+            if (visibleInsets == null) visibleInsets = new Rect(0,0,0,0);
+            if (touchableRegion == null) {
+                touchableRegion = mTouchableRegion;
+            } else if (touchableRegion != null && mTouchableRegion != null) {
+                touchableRegion.op(touchableRegion, mTouchableRegion, Region.Op.UNION);
+            }
+            try {
+                mWindowSession.setInsets(mWindow, touchableInsetMode,
+                                         contentInsets, visibleInsets, touchableRegion);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else if (mTouchableRegion == null && mPreviousTouchableRegion != null) {
+            mPreviousTouchableRegion = null;
+            try {
+                mWindowSession.clearTouchableRegion(mWindow);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
             }
         }
 
@@ -7928,22 +7971,25 @@
             }
         }
 
-        long frameNumber = -1;
-        if (mSurface.isValid()) {
-            frameNumber = mSurface.getNextFrameNumber();
-        }
+        final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f);
+        final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f);
 
         int relayoutResult = mWindowSession.relayout(mWindow, params,
-                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
-                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
-                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
+                requestedWidth, requestedHeight, viewVisibility,
+                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
-                mTempControls, mSurfaceSize);
+                mTempControls);
 
         final int transformHint = SurfaceControl.rotationToBufferTransform(
                 (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
         mSurfaceControl.setTransformHint(transformHint);
 
+        final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
+        final boolean dragResizing = (relayoutResult
+                & (RELAYOUT_RES_DRAG_RESIZING_DOCKED | RELAYOUT_RES_DRAG_RESIZING_FREEFORM)) != 0;
+        WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth,
+                requestedHeight, mTmpFrames.frame, dragResizing, mSurfaceSize);
+
         if (mAttachInfo.mContentCaptureManager != null) {
             MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
                     .getMainContentCaptureSession();
@@ -10701,4 +10747,15 @@
         }
         return mFallbackOnBackInvokedDispatcher;
     }
+
+    @Override
+    public void setTouchableRegion(Region r) {
+        if (r != null) {
+            mTouchableRegion = new Region(r);
+        } else {
+            mTouchableRegion = null;
+        }
+        mLastGivenInsets.reset();
+        requestLayout();
+    }
 }
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index e5c7d6d..27f89ec 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -36,6 +36,7 @@
 import android.app.WindowConfiguration;
 import android.app.WindowConfiguration.WindowingMode;
 import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Log;
 
@@ -258,6 +259,7 @@
                 + " outFrame=" + outFrame.toShortString()
                 + " outParentFrame=" + outParentFrame.toShortString()
                 + " outDisplayFrame=" + outDisplayFrame.toShortString()
+                + " windowBounds=" + windowBounds.toShortString()
                 + " attachedWindowFrame=" + (attachedWindowFrame != null
                         ? attachedWindowFrame.toShortString()
                         : "null")
@@ -272,4 +274,45 @@
 
         return clippedByDisplayCutout;
     }
+
+    public static void computeSurfaceSize(WindowManager.LayoutParams attrs, Rect maxBounds,
+            int requestedWidth, int requestedHeight, Rect winFrame, boolean dragResizing,
+            Point outSurfaceSize) {
+        int width;
+        int height;
+        if ((attrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0) {
+            // For a scaled surface, we always want the requested size.
+            width = requestedWidth;
+            height = requestedHeight;
+        } else {
+            // When we're doing a drag-resizing, request a surface that's fullscreen size,
+            // so that we don't need to reallocate during the process. This also prevents
+            // buffer drops due to size mismatch.
+            if (dragResizing) {
+                // The maxBounds should match the display size which applies fixed-rotation
+                // transformation if there is any.
+                width = maxBounds.width();
+                height = maxBounds.height();
+            } else {
+                width = winFrame.width();
+                height = winFrame.height();
+            }
+        }
+
+        // This doesn't necessarily mean that there is an error in the system. The sizes might be
+        // incorrect, because it is before the first layout or draw.
+        if (width < 1) {
+            width = 1;
+        }
+        if (height < 1) {
+            height = 1;
+        }
+
+        // Adjust for surface insets.
+        final Rect surfaceInsets = attrs.surfaceInsets;
+        width += surfaceInsets.left + surfaceInsets.right;
+        height += surfaceInsets.top + surfaceInsets.bottom;
+
+        outSurfaceSize.set(width, height);
+    }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 998498b..3392edc 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
@@ -264,10 +263,10 @@
 
     @Override
     public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
-            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
+            int requestedWidth, int requestedHeight, int viewFlags, int flags,
             ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
+            InsetsSourceControl[] outActiveControls) {
         final State state;
         synchronized (this) {
             state = mStateForWindow.get(window.asBinder());
@@ -286,7 +285,6 @@
         WindowManager.LayoutParams attrs = state.mParams;
 
         if (viewFlags == View.VISIBLE) {
-            outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
             t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
             outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
         } else {
@@ -335,6 +333,11 @@
     }
 
     @Override
+    public void clearTouchableRegion(android.view.IWindow window) {
+        setTouchRegion(window.asBinder(), null);
+    }
+
+    @Override
     public void finishDrawing(android.view.IWindow window,
             android.view.SurfaceControl.Transaction postDrawTransaction) {
         synchronized (this) {
@@ -480,17 +483,6 @@
         return null;
     }
 
-    private int getSurfaceWidth(WindowManager.LayoutParams attrs) {
-      final Rect surfaceInsets = attrs.surfaceInsets;
-      return surfaceInsets != null
-          ? attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
-    }
-    private int getSurfaceHeight(WindowManager.LayoutParams attrs) {
-      final Rect surfaceInsets = attrs.surfaceInsets;
-      return surfaceInsets != null
-          ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
-    }
-
     @Override
     public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
                                          boolean grantFocus) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6a22023..849f3c9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE;
+import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
@@ -1793,6 +1795,14 @@
                 Log.w(TAG, "Ignoring startStylusHandwriting: View's window does not have focus.");
                 return;
             }
+            if (mServedInputConnection != null && getDelegate().hasActiveConnection(view)) {
+                // TODO (b/210039666): optimize CURSOR_UPDATE_IMMEDIATE.
+                // TODO (b/215533103): Introduce new modes in requestCursorUpdates().
+                // TODO (b/210039666): Pipe IME displayId from InputBindResult and use it here.
+                //  instead of mDisplayId.
+                mServedInputConnection.requestCursorUpdatesFromImm(
+                        CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR, mDisplayId);
+            }
 
             try {
                 mService.startStylusHandwriting(mClient);
@@ -2419,9 +2429,9 @@
     public boolean isCursorAnchorInfoEnabled() {
         synchronized (mH) {
             final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
-                    InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
+                    CURSOR_UPDATE_IMMEDIATE) != 0;
             final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode &
-                    InputConnection.CURSOR_UPDATE_MONITOR) != 0;
+                    CURSOR_UPDATE_MONITOR) != 0;
             return isImmediate || isMonitoring;
         }
     }
@@ -2493,7 +2503,7 @@
             // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
             // not been changed from the previous call.
             final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode &
-                    InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
+                    CURSOR_UPDATE_IMMEDIATE) != 0;
             if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) {
                 // TODO: Consider always emitting this message once we have addressed redundant
                 // calls of this method from android.widget.Editor.
@@ -2507,7 +2517,7 @@
             mCurrentInputMethodSession.updateCursorAnchorInfo(cursorAnchorInfo);
             mCursorAnchorInfo = cursorAnchorInfo;
             // Clear immediate bit (if any).
-            mRequestUpdateCursorAnchorInfoMonitorMode &= ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
+            mRequestUpdateCursorAnchorInfoMonitorMode &= ~CURSOR_UPDATE_IMMEDIATE;
         }
     }
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 364ba50..f14c251 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 
@@ -135,6 +137,20 @@
     public @interface CacheMode {}
 
     /**
+     * Enable web content to apply light or dark style according to the app's theme
+     * and WebView to attempt to darken web content by algorithmic darkening when
+     * appropriate.
+     *
+     * Refer to {@link #setAllowAlgorithmicDarkening} for detail.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
+    @SystemApi
+    public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L;
+
+    /**
      * Default cache usage mode. If the navigation type doesn't impose any
      * specific behavior, use cached resources when they are available
      * and not expired, otherwise load resources from the network.
@@ -239,6 +255,7 @@
      * automatically darkened.
      *
      * @see #setForceDark
+     * @deprecated refer to {@link #setForceDark}
      */
     public static final int FORCE_DARK_OFF = 0;
 
@@ -250,6 +267,7 @@
      * be inverted.
      *
      * @see #setForceDark
+     * @deprecated refer to {@link #setForceDark}
      */
     public static final int FORCE_DARK_AUTO = 1;
 
@@ -258,6 +276,7 @@
      * as to emulate a dark theme.
      *
      * @see #setForceDark
+     * @deprecated refer to {@link #setForceDark}
      */
     public static final int FORCE_DARK_ON = 2;
 
@@ -1533,6 +1552,13 @@
      *
      * @param forceDark the force dark mode to set.
      * @see #getForceDark
+     * @deprecated The "force dark" model previously implemented by WebView was complex
+     * and didn't interoperate well with current Web standards for
+     * prefers-color-scheme and color-scheme. In apps with
+     * {@code targetSdkVersion} &ge; {@link android.os.Build.VERSION_CODES#TIRAMISU}
+     * this API is a no-op and WebView will always use the dark style defined by web content
+     * authors if the app's theme is dark. To customize the behavior, refer to
+     * {@link #setAllowAlgorithmicDarkening}.
      */
     public void setForceDark(@ForceDark int forceDark) {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1544,6 +1570,7 @@
      *
      * @return the currently set force dark mode.
      * @see #setForceDark
+     * @deprecated refer to {@link #setForceDark}.
      */
     public @ForceDark int getForceDark() {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1551,6 +1578,49 @@
     }
 
     /**
+     * Control whether algorithmic darkening is allowed.
+     *
+     * <p class="note">
+     * <b>Note:</b> This API and the behaviour described only apply to apps with
+     * {@code targetSdkVersion} &ge; {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+     *
+     * <p>
+     * WebView always sets the media query {@code prefers-color-scheme} according to the app's
+     * theme attribute {@link android.R.styleable#Theme_isLightTheme isLightTheme}, i.e.
+     * {@code prefers-color-scheme} is {@code light} if isLightTheme is true or not specified,
+     * otherwise it is {@code dark}. This means that the web content's light or dark style will
+     * be applied automatically to match the app's theme if the content supports it.
+     *
+     * <p>
+     * Algorithmic darkening is disallowed by default.
+     * <p>
+     * If the app's theme is dark and it allows algorithmic darkening, WebView will attempt to
+     * darken web content using an algorithm, if the content doesn't define its own dark styles
+     * and doesn't explicitly disable darkening.
+     *
+     * <p>
+     * If Android is applying Force Dark to WebView then WebView will ignore the value of
+     * this setting and behave as if it were set to true.
+     *
+     * @param allow allow algorithmic darkening or not.
+     */
+    public void setAllowAlgorithmicDarkening(boolean allow) {
+        // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
+    }
+
+    /**
+     * Get if algorithmic darkening is allowed or not for this WebView.
+     * The default is false.
+     *
+     * @return if the algorithmic darkening is allowed or not.
+     * @see #setAllowAlgorithmicDarkening
+     */
+    public boolean getAllowAlgorithmicDarkening() {
+        // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
+        return false;
+    }
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "MENU_ITEM_" }, value = {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b21d08c..c6f64f4 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1517,6 +1517,12 @@
         }
     }
 
+    /**
+     * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
+     * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
+     * unexpectedly.
+     */
+    @Deprecated
     private final class ViewContentNavigation extends Action {
         final boolean mNext;
 
@@ -4121,7 +4127,11 @@
      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
      *
      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
+     * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
+     * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
+     * unexpectedly.
      */
+    @Deprecated
     public void showNext(@IdRes int viewId) {
         addAction(new ViewContentNavigation(viewId, true /* next */));
     }
@@ -4130,7 +4140,11 @@
      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
      *
      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
+     * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
+     * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
+     * unexpectedly.
      */
+    @Deprecated
     public void showPrevious(@IdRes int viewId) {
         addAction(new ViewContentNavigation(viewId, false /* next */));
     }
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index cfccb71..fdaab66 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -17,6 +17,8 @@
 
 import static android.view.WindowManagerImpl.createWindowContextWindowManager;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
@@ -135,7 +137,8 @@
     }
 
     /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */
-    void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
         mCallbacksController.dispatchConfigurationChanged(newConfig);
     }
 
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 0470444..d1942ac 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -932,6 +932,24 @@
         });
     }
 
+    /**
+     * Dispatches {@link InputConnection#requestCursorUpdates(int)}.
+     *
+     * <p>This method is intended to be called only from {@link InputMethodManager}.</p>
+     * @param cursorUpdateMode the mode for {@link InputConnection#requestCursorUpdates(int)}
+     * @param imeDisplayId displayId on which IME is displayed.
+     */
+    @Dispatching(cancellable = true)
+    public void requestCursorUpdatesFromImm(int cursorUpdateMode, int imeDisplayId) {
+        final int currentSessionId = mCurrentSessionId.get();
+        dispatchWithTracing("requestCursorUpdatesFromImm", () -> {
+            if (currentSessionId != mCurrentSessionId.get()) {
+                return;  // cancelled
+            }
+            requestCursorUpdatesInternal(cursorUpdateMode, imeDisplayId);
+        });
+    }
+
     @Dispatching(cancellable = true)
     @Override
     public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
@@ -940,24 +958,28 @@
             if (header.mSessionId != mCurrentSessionId.get()) {
                 return false;  // cancelled
             }
-            final InputConnection ic = getInputConnection();
-            if (ic == null || !isActive()) {
-                Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
-                return false;
-            }
-            if (mParentInputMethodManager.getDisplayId() != imeDisplayId) {
-                // requestCursorUpdates() is not currently supported across displays.
-                return false;
-            }
-            try {
-                return ic.requestCursorUpdates(cursorUpdateMode);
-            } catch (AbstractMethodError ignored) {
-                // TODO(b/199934664): See if we can remove this by providing a default impl.
-                return false;
-            }
+            return requestCursorUpdatesInternal(cursorUpdateMode, imeDisplayId);
         });
     }
 
+    private boolean requestCursorUpdatesInternal(int cursorUpdateMode, int imeDisplayId) {
+        final InputConnection ic = getInputConnection();
+        if (ic == null || !isActive()) {
+            Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+            return false;
+        }
+        if (mParentInputMethodManager.getDisplayId() != imeDisplayId) {
+            // requestCursorUpdates() is not currently supported across displays.
+            return false;
+        }
+        try {
+            return ic.requestCursorUpdates(cursorUpdateMode);
+        } catch (AbstractMethodError ignored) {
+            // TODO(b/199934664): See if we can remove this by providing a default impl.
+            return false;
+        }
+    }
+
     @Dispatching(cancellable = true)
     @Override
     public void commitContent(InputConnectionCommandHeader header,
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 998ec96..51e150e 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -249,7 +249,8 @@
 
     optional android.service.NetworkStatsServiceDumpProto netstats = 3001 [
         (section).type = SECTION_DUMPSYS,
-        (section).args = "netstats --proto"
+        (section).args = "netstats --proto",
+        (section).userdebug_and_eng_only = true
     ];
 
     optional android.providers.settings.SettingsServiceDumpProto settings = 3002 [
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 11560a5..c33b7c9 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -69,7 +69,7 @@
     // know what activity types to check for when invoking splitscreen multi-window.
     optional bool is_home_recents_component = 6;
     repeated IdentifierProto pending_activities = 7 [deprecated=true];
-    optional int32 default_min_size_resizable_task = 8;
+    optional int32 default_min_size_resizable_task = 8 [deprecated=true];
 }
 
 message BarControllerProto {
@@ -226,7 +226,7 @@
     optional bool is_sleeping = 36;
     repeated string sleep_tokens = 37;
     repeated .android.graphics.RectProto keep_clear_areas = 38;
-
+    optional int32 min_size_of_resizeable_task_dp = 39;
 }
 
 /* represents DisplayArea object */
@@ -439,7 +439,7 @@
     optional bool is_on_screen = 37;
     optional bool is_visible = 38;
     optional bool pending_seamless_rotation = 39;
-    optional int64 finished_seamless_rotation_frame = 40;
+    optional int64 finished_seamless_rotation_frame = 40 [deprecated=true];
     optional WindowFramesProto window_frames = 41;
     optional bool force_seamless_rotation = 42;
     optional bool has_compat_scale = 43;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3a842ee..f106872 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -974,6 +974,61 @@
         android:permissionFlags="softRestricted|immutablyRestricted"
         android:protectionLevel="dangerous" />
 
+    <!-- Required to be able to read audio files from shared storage.
+     <p>Protection level: dangerous -->
+    <permission-group android:name="android.permission-group.READ_MEDIA_AURAL"
+                      android:icon="@drawable/perm_group_read_media_aural"
+                      android:label="@string/permgrouplab_readMediaAural"
+                      android:description="@string/permgroupdesc_readMediaAural"
+                      android:priority="950" />
+
+    <!-- Allows an application to read audio files from external storage.
+      <p>This permission is enforced starting in API level
+      {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+      For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+      targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+      must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+     <p>Protection level: dangerous -->
+    <permission android:name="android.permission.READ_MEDIA_AUDIO"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_readMediaAudio"
+                android:description="@string/permdesc_readMediaAudio"
+                android:protectionLevel="dangerous" />
+
+    <!-- Required to be able to read image and video files from shared storage.
+     <p>Protection level: dangerous -->
+    <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL"
+                      android:icon="@drawable/perm_group_read_media_visual"
+                      android:label="@string/permgrouplab_readMediaVisual"
+                      android:description="@string/permgroupdesc_readMediaVisual"
+                      android:priority="1000" />
+
+    <!-- Allows an application to read audio files from external storage.
+    <p>This permission is enforced starting in API level
+    {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+    For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+    targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+    must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+   <p>Protection level: dangerous -->
+    <permission android:name="android.permission.READ_MEDIA_VIDEO"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_readMediaVideo"
+                android:description="@string/permdesc_readMediaVideo"
+                android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to read image files from external storage.
+      <p>This permission is enforced starting in API level
+      {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+      For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+      targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+      must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+     <p>Protection level: dangerous -->
+    <permission android:name="android.permission.READ_MEDIA_IMAGE"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_readMediaImage"
+                android:description="@string/permdesc_readMediaImage"
+                android:protectionLevel="dangerous" />
+
     <!-- Allows an application to write to external storage.
          <p class="note"><strong>Note:</strong> If <em>both</em> your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
@@ -2306,6 +2361,26 @@
     <permission android:name="android.permission.MANAGE_FACTORY_RESET_PROTECTION"
         android:protectionLevel="signature|privileged"/>
 
+    <!-- ======================================== -->
+    <!-- Permissions for lost mode -->
+    <!-- ======================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application to trigger lost mode on an organization-owned device.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.TRIGGER_LOST_MODE"
+        android:protectionLevel="signature|role"/>
+
+    <!-- @SystemApi Allows an application to instruct the framework to send location to the device
+         admin when an organization-owned device is in lost mode.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES"
+        android:protectionLevel="signature|privileged"/>
+
     <!-- ================================== -->
     <!-- Permissions for accessing hardware -->
     <!-- ================================== -->
@@ -6200,6 +6275,9 @@
          <p>Not for use by third-party applications.</p> -->
     <attribution android:tag="MusicRecognitionManagerService"
         android:label="@string/music_recognition_manager_service"/>
+    <!-- Attribution for Device Policy Manager service. -->
+    <attribution android:tag="DevicePolicyManagerService"
+        android:label="@string/device_policy_manager_service"/>
 
     <application android:process="system"
                  android:persistent="true"
@@ -6691,7 +6769,7 @@
         </service>
 
         <!-- TODO: Move to ExtServices or relevant component. -->
-        <service android:name="com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService"
+        <service android:name="android.service.selectiontoolbar.DefaultSelectionToolbarRenderService"
                  android:permission="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
                  android:process=":ui"
                  android:exported="false">
diff --git a/core/res/res/drawable/perm_group_read_media_aural.xml b/core/res/res/drawable/perm_group_read_media_aural.xml
new file mode 100644
index 0000000..6fc9c69
--- /dev/null
+++ b/core/res/res/drawable/perm_group_read_media_aural.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10,21q-1.65,0 -2.825,-1.175Q6,18.65 6,17q0,-1.65 1.175,-2.825Q8.35,13 10,13q0.575,0 1.063,0.137 0.487,0.138 0.937,0.413V3h6v4h-4v10q0,1.65 -1.175,2.825Q11.65,21 10,21z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/perm_group_read_media_visual.xml b/core/res/res/drawable/perm_group_read_media_visual.xml
new file mode 100644
index 0000000..a5db271
--- /dev/null
+++ b/core/res/res/drawable/perm_group_read_media_visual.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 249881e..02d6293d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -134,9 +134,6 @@
          be sent during a change to the audio output device. -->
     <bool name="config_sendAudioBecomingNoisy">true</bool>
 
-    <!-- Whether Hearing Aid profile is supported -->
-    <bool name="config_hearing_aid_profile_supported">false</bool>
-
     <!-- Flag to disable all transition animations -->
     <bool name="config_disableTransitionAnimation">false</bool>
 
@@ -404,10 +401,6 @@
     <string-array translatable="false" name="config_tether_bluetooth_regexs">
     </string-array>
 
-    <!-- Max number of Bluetooth tethering connections allowed. If this is
-         updated config_tether_dhcp_range has to be updated appropriately. -->
-    <integer translatable="false" name="config_max_pan_devices">5</integer>
-
     <!-- This setting is deprecated, please use
          com.android.networkstack.tethering.R.array.config_dhcp_range instead. -->
     <string-array translatable="false" name="config_tether_dhcp_range">
@@ -1944,53 +1937,38 @@
     <!-- Integer to set a max latency the accelerometer will batch sensor requests with. -->
     <integer name="config_flipToScreenOffMaxLatencyMicros">2000000</integer>
 
-    <!-- Boolean indicating if current platform supports bluetooth SCO for off call
-    use cases -->
+    <!-- Note: This config is deprecated
+          Boolean indicating if current platform supports bluetooth SCO for off call
+          use cases
+    -->
     <bool name="config_bluetooth_sco_off_call">true</bool>
 
-    <!-- Boolean indicating if current platform supports bluetooth wide band
-         speech -->
-    <bool name="config_bluetooth_wide_band_speech">true</bool>
-
-    <!-- Boolean indicating if current platform need do one-time bluetooth address
-         re-validation -->
+    <!-- Note: This config is deprecated
+          Boolean indicating if current platform need do one-time bluetooth address
+          re-validation
+    -->
     <bool name="config_bluetooth_address_validation">false</bool>
 
-    <!-- Boolean indicating if current platform supports BLE peripheral mode -->
-    <bool name="config_bluetooth_le_peripheral_mode_supported">false</bool>
-
-    <!-- Boolean indicating if current platform supports HFP inband ringing -->
-    <bool name="config_bluetooth_hfp_inband_ringing_support">false</bool>
-
-    <!-- Max number of scan filters supported by blutooth controller. 0 if the
-         device does not support hardware scan filters-->
-    <integer translatable="false" name="config_bluetooth_max_scan_filters">0</integer>
-
-    <!-- Max number of advertisers supported by bluetooth controller. 0 if the
-         device does not support multiple advertisement-->
-    <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer>
-
-    <!-- Idle current for bluetooth controller. 0 by default-->
+    <!-- Note: This config is deprecated, use BluetoothProperties instead.
+         Idle current for bluetooth controller. 0 by default
+    -->
     <integer translatable="false" name="config_bluetooth_idle_cur_ma">0</integer>
 
-    <!-- Rx current for bluetooth controller. 0 by default-->
+    <!-- Note: This config is deprecated, use BluetoothProperties instead.
+         Rx current for bluetooth controller. 0 by default
+    -->
     <integer translatable="false" name="config_bluetooth_rx_cur_ma">0</integer>
 
-    <!-- Tx current for bluetooth controller. 0 by default-->
+    <!-- Note: This config is deprecated, use BluetoothProperties instead.
+         Tx current for bluetooth controller. 0 by default
+    -->
     <integer translatable="false" name="config_bluetooth_tx_cur_ma">0</integer>
 
-    <!-- Operating volatage for bluetooth controller. 0 by default-->
+    <!-- Note: This config is deprecated, use BluetoothProperties instead.
+         Operating volatage for bluetooth controller. 0 by default
+    -->
     <integer translatable="false" name="config_bluetooth_operating_voltage_mv">0</integer>
 
-    <!-- Max number of connected audio devices supported by Bluetooth stack -->
-    <integer name="config_bluetooth_max_connected_audio_devices">5</integer>
-
-    <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
-    <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
-
-    <!-- Enabling autoconnect over pan -->
-    <bool name="config_bluetooth_pan_enable_autoconnect">false</bool>
-
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
@@ -2144,10 +2122,6 @@
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
 
-    <!-- Enable/disable default bluetooth profiles:
-        HSP_AG, ObexObjectPush, Audio, NAP -->
-    <bool name="config_bluetooth_default_profiles">true</bool>
-
     <!-- IP address of the dns server to use if nobody else suggests one -->
     <string name="config_default_dns_server" translatable="false">8.8.8.8</string>
 
@@ -4357,8 +4331,6 @@
     <!-- Component name that should be granted Notification Assistant access -->
     <string name="config_defaultAssistantAccessComponent" translatable="false">android.ext.services/android.ext.services.notification.Assistant</string>
 
-    <bool name="config_supportBluetoothPersistedState">true</bool>
-
     <bool name="config_keepRestrictedProfilesInBackground">true</bool>
 
     <!-- Cellular network service package name to bind to by default. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1a5d8b7..59ad302 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -197,7 +197,7 @@
     <string name="ThreeWCMmi">Three way calling</string>
     <string name="RuacMmi">Rejection of undesired annoying calls</string>
     <string name="CndMmi">Calling number delivery</string>
-    <string name="DndMmi">Do not disturb</string>
+    <string name="DndMmi" translatable="false">Priority mode</string>
 
     <!-- Displayed to confirm to the user that caller ID will be restricted on the next call as usual. -->
     <string name="CLIRDefaultOnNextCallOn">Caller ID defaults to restricted. Next call: Restricted</string>
@@ -521,6 +521,8 @@
     <string name="twilight_service">Twilight Service</string>
     <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
     <string name="gnss_time_update_service">GNSS Time Update Service</string>
+    <!-- Attribution for Device Policy Manager service. [CHAR LIMIT=NONE]-->
+    <string name="device_policy_manager_service">Device Policy Manager Service</string>
 
     <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]-->
     <string name="music_recognition_manager_service">Music Recognition Manager Service</string>
@@ -876,6 +878,16 @@
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_storage">access photos, media, and files on your device</string>
 
+    <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
+    <string name="permgrouplab_readMediaAural">Music &amp; other audio</string>
+    <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
+    <string name="permgroupdesc_readMediaAural">access audio files on your device</string>
+
+    <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
+    <string name="permgrouplab_readMediaVisual">Photos &amp; videos</string>
+    <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
+    <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string>
+
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_microphone">Microphone</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1889,6 +1901,21 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
     <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permlab_readMediaAudio">read audio files from shared storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permdesc_readMediaAudio">Allows the app to read audio files from your shared storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permlab_readMediaVideo">read video files from shared storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permdesc_readMediaVideo">Allows the app to read video files from your shared storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permlab_readMediaImage">read image files from shared storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permdesc_readMediaImage">Allows the app to read image files from your shared storage.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
     <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
@@ -2002,9 +2029,9 @@
     <string name="permdesc_bindCarrierServices">Allows the holder to bind to carrier services. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, for applications that wish to access notification policy. -->
-    <string name="permlab_access_notification_policy">access Do Not Disturb</string>
+    <string name="permlab_access_notification_policy" translatable="false">access Priority mode</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
+    <string name="permdesc_access_notification_policy" translatable="false">Allows the app to read and write Priority mode configuration.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_startViewPermissionUsage">start view permission usage</string>
@@ -5267,7 +5294,7 @@
     <string name="zen_mode_forever">Until you turn off</string>
 
     <!-- Zen mode condition: no exit criteria, includes the name of the feature for emphasis. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_forever_dnd">Until you turn off Do Not Disturb</string>
+    <string name="zen_mode_forever_dnd" translatable="false">Until you turn off Priority mode</string>
 
     <!-- Zen mode active automatic rule name separator. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_rule_name_combination"><xliff:g id="first" example="Weeknights">%1$s</xliff:g> / <xliff:g id="rest" example="Meetings">%2$s</xliff:g></string>
@@ -5276,7 +5303,7 @@
     <string name="toolbar_collapse_description">Collapse</string>
 
     <!-- Zen mode - feature name. [CHAR LIMIT=40] -->
-    <string name="zen_mode_feature_name">Do not disturb</string>
+    <string name="zen_mode_feature_name" translatable="false">Priority mode</string>
 
     <!-- Zen mode - downtime legacy feature name. [CHAR LIMIT=40] -->
     <string name="zen_mode_downtime_feature_name">Downtime</string>
@@ -5692,14 +5719,14 @@
 
     <!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
     <string name="notification_channel_system_changes">System changes</string>
-    <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
-    <string name="notification_channel_do_not_disturb">Do Not Disturb</string>
-    <!-- Title of notification indicating do not disturb visual interruption settings have changed when upgrading to P -->
-    <string name="zen_upgrade_notification_visd_title">New: Do Not Disturb is hiding notifications</string>
+    <!-- Title for the notification channel notifying user of priority mode system changes (i.e. Priority mode has changed). [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_do_not_disturb" translatable="false">Priority mode</string>
+    <!-- Title of notification indicating Priority mode visual interruption settings have changed when upgrading to P -->
+    <string name="zen_upgrade_notification_visd_title" translatable="false">New: Priority mode is hiding notifications</string>
     <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
     <string name="zen_upgrade_notification_visd_content">Tap to learn more and change.</string>
-    <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
-    <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
+    <!-- Title of notification indicating priority mode settings have changed when upgrading to P -->
+    <string name="zen_upgrade_notification_title" translatable="false">Priority mode has changed</string>
     <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
     <string name="zen_upgrade_notification_content">Tap to check what\'s blocked.</string>
 
@@ -5740,7 +5767,7 @@
     <!-- Label of notification action button to learn more about the enhanced notifications [CHAR LIMIT=20] -->
     <string name="nas_upgrade_notification_learn_more_action">Learn more</string>
     <!-- Content of notification learn more dialog about the enhanced notifications [CHAR LIMIT=NONE] -->
-    <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Do Not Disturb.</string>
+    <string name="nas_upgrade_notification_learn_more_content" translatable="false">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Priority mode.</string>
 
 
     <!-- Dynamic mode battery saver strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bcc3a6d..4d8f9a2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -279,8 +279,6 @@
   <java-symbol type="bool" name="config_flipToScreenOffEnabled" />
   <java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
-  <java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" />
-  <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" />
   <java-symbol type="bool" name="config_cellBroadcastAppLinks" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_disableTransitionAnimation" />
@@ -344,7 +342,6 @@
   <java-symbol type="integer" name="config_timeZoneRulesCheckRetryCount" />
   <java-symbol type="bool" name="config_sendAudioBecomingNoisy" />
   <java-symbol type="bool" name="config_enableScreenshotChord" />
-  <java-symbol type="bool" name="config_bluetooth_default_profiles" />
   <java-symbol type="bool" name="config_enableWifiDisplay" />
   <java-symbol type="bool" name="config_allowAnimationsInLowPowerMode" />
   <java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
@@ -418,9 +415,6 @@
   <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
   <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" />
   <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
-  <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
-  <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
-  <java-symbol type="integer" name="config_bluetooth_max_connected_audio_devices" />
   <java-symbol type="integer" name="config_burnInProtectionMinHorizontalOffset" />
   <java-symbol type="integer" name="config_burnInProtectionMaxHorizontalOffset" />
   <java-symbol type="integer" name="config_burnInProtectionMinVerticalOffset" />
@@ -430,9 +424,6 @@
   <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
-  <java-symbol type="bool" name="config_bluetooth_pan_enable_autoconnect" />
-  <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
-  <java-symbol type="bool" name="config_hearing_aid_profile_supported" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
   <java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -451,7 +442,6 @@
   <java-symbol type="integer" name="config_wakeUpToLastStateTimeoutMillis" />
   <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
   <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
-  <java-symbol type="integer" name="config_max_pan_devices" />
   <java-symbol type="integer" name="config_ntpPollingInterval" />
   <java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
   <java-symbol type="integer" name="config_ntpRetry" />
@@ -3842,8 +3832,6 @@
 
   <java-symbol type="string" name="config_defaultAssistantAccessComponent" />
 
-  <java-symbol type="bool" name="config_supportBluetoothPersistedState" />
-
   <java-symbol type="string" name="slices_permission_request" />
 
   <java-symbol type="string" name="screenshot_edit" />
diff --git a/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java b/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java
index 09985a8..aa188f2 100644
--- a/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java
+++ b/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java
@@ -116,25 +116,4 @@
         @Override
         public void onLowMemory() {}
     }
-
-    private static class TestComponentCallbacks2 implements ComponentCallbacks2 {
-        private Configuration mConfiguration;
-        private boolean mLowMemoryCalled;
-        private int mLevel;
-
-        @Override
-        public void onConfigurationChanged(@NonNull Configuration newConfig) {
-            mConfiguration = newConfig;
-        }
-
-        @Override
-        public void onLowMemory() {
-            mLowMemoryCalled = true;
-        }
-
-        @Override
-        public void onTrimMemory(int level) {
-            mLevel = level;
-        }
-    }
 }
diff --git a/core/tests/coretests/src/android/content/ContextWrapperTest.java b/core/tests/coretests/src/android/content/ContextWrapperTest.java
new file mode 100644
index 0000000..ecaf1f4
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContextWrapperTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 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;
+
+import static android.content.ContextWrapper.COMPONENT_CALLBACK_ON_WRAPPER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.WindowConfiguration;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.window.WindowContext;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+
+/**
+ *  Build/Install/Run:
+ *   atest FrameworksCoreTests:ContextWrapperTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ContextWrapperTest {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    /**
+     * Before {@link android.os.Build.VERSION_CODES#TIRAMISU}, {@link ContextWrapper} must
+     * register {@link ComponentCallbacks} to {@link ContextWrapper#getApplicationContext} before
+     * {@link ContextWrapper#attachBaseContext(Context)}.
+     */
+    @DisableCompatChanges(COMPONENT_CALLBACK_ON_WRAPPER)
+    @Test
+    public void testRegisterComponentCallbacksWithoutBaseContextBeforeT() {
+        final ContextWrapper wrapper = new TestContextWrapper(null /* base */);
+        final ComponentCallbacks callbacks = new TestComponentCallbacks2();
+
+        // It should be no-op if unregister a ComponentCallbacks without registration.
+        wrapper.unregisterComponentCallbacks(callbacks);
+
+        wrapper.registerComponentCallbacks(callbacks);
+
+        assertThat(wrapper.mCallbacksRegisteredToSuper.size()).isEqualTo(1);
+        assertThat(wrapper.mCallbacksRegisteredToSuper.get(0)).isEqualTo(callbacks);
+
+        wrapper.unregisterComponentCallbacks(callbacks);
+
+        assertThat(wrapper.mCallbacksRegisteredToSuper.isEmpty()).isTrue();
+    }
+
+    /**
+     * After {@link android.os.Build.VERSION_CODES#TIRAMISU}, {@link ContextWrapper} must
+     * throw {@link IllegalStateException} before {@link ContextWrapper#attachBaseContext(Context)}.
+     */
+    @Test
+    public void testRegisterComponentCallbacksWithoutBaseContextAfterT() {
+        final ContextWrapper wrapper = new TestContextWrapper(null /* base */);
+        final ComponentCallbacks callbacks = new TestComponentCallbacks2();
+
+        try {
+            wrapper.unregisterComponentCallbacks(callbacks);
+            fail("ContextWrapper#unregisterComponentCallbacks must throw Exception before"
+                    + " ContextWrapper#attachToBaseContext.");
+        } catch (IllegalStateException ignored) {
+            // It is expected to throw IllegalStateException.
+        }
+
+        try {
+            wrapper.registerComponentCallbacks(callbacks);
+            fail("ContextWrapper#registerComponentCallbacks must throw Exception before"
+                    + " ContextWrapper#attachToBaseContext.");
+        } catch (IllegalStateException ignored) {
+            // It is expected to throw IllegalStateException.
+        }
+    }
+
+    /**
+     * {@link ContextWrapper#registerComponentCallbacks(ComponentCallbacks)} must delegate to its
+     * {@link ContextWrapper#getBaseContext()}, so does
+     * {@link ContextWrapper#unregisterComponentCallbacks(ComponentCallbacks)}.
+     */
+    @Test
+    public void testRegisterComponentCallbacks() {
+        final Context appContext = ApplicationProvider.getApplicationContext();
+        final Display display = appContext.getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY);
+        final WindowContext windowContext = (WindowContext) appContext.createWindowContext(display,
+                TYPE_APPLICATION_OVERLAY, null /* options */);
+        final ContextWrapper wrapper = new ContextWrapper(windowContext);
+        final TestComponentCallbacks2 callbacks = new TestComponentCallbacks2();
+
+        wrapper.registerComponentCallbacks(callbacks);
+
+        assertThat(wrapper.mCallbacksRegisteredToSuper).isNull();
+
+        final Configuration dispatchedConfig = new Configuration();
+        dispatchedConfig.fontScale = 1.2f;
+        dispatchedConfig.windowConfiguration.setWindowingMode(
+                WindowConfiguration.WINDOWING_MODE_FREEFORM);
+        dispatchedConfig.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
+        windowContext.dispatchConfigurationChanged(dispatchedConfig);
+
+        assertThat(callbacks.mConfiguration).isEqualTo(dispatchedConfig);
+    }
+
+    private static class TestContextWrapper extends ContextWrapper {
+        TestContextWrapper(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Context getApplicationContext() {
+            // The default implementation of ContextWrapper#getApplicationContext is to delegate
+            // to the base Context, and it leads to NPE if #registerComponentCallbacks is called
+            // directly before attach to base Context.
+            // We call to ApplicationProvider#getApplicationContext to prevent NPE because
+            // developers may have its implementation to prevent NPE without attaching base Context.
+            final Context baseContext = getBaseContext();
+            if (baseContext == null) {
+                return ApplicationProvider.getApplicationContext();
+            } else {
+                return super.getApplicationContext();
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/content/TestComponentCallbacks2.java b/core/tests/coretests/src/android/content/TestComponentCallbacks2.java
new file mode 100644
index 0000000..6ae7fc4
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TestComponentCallbacks2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.content.res.Configuration;
+
+import androidx.annotation.NonNull;
+
+class TestComponentCallbacks2 implements ComponentCallbacks2 {
+    android.content.res.Configuration mConfiguration;
+    boolean mLowMemoryCalled;
+    int mLevel;
+
+    @Override
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
+        mConfiguration = newConfig;
+    }
+
+    @Override
+    public void onLowMemory() {
+        mLowMemoryCalled = true;
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        mLevel = level;
+    }
+}
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index a5261ae..4319b97 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -19,6 +19,10 @@
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.extractBoundsFromList;
 import static android.view.DisplayCutout.fromSpec;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
@@ -497,6 +501,74 @@
                 new ParcelableWrapper(mCutoutNumbers));
     }
 
+    @Test
+    public void testGetRotatedBounds_top_rot0() {
+        int displayW = 500, displayH = 1000;
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0));
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0));
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_0);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot90() {
+        int displayW = 500, displayH = 1000;
+        DisplayCutout expected = new DisplayCutout(Insets.of(100, 20, 0, 20),
+                new Rect(0, displayW - 75, 100, displayW - 50), ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_90));
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0));
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_90);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot180() {
+        int displayW = 500, displayH = 1000;
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180));
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0));
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_180);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot270() {
+        int displayW = 500, displayH = 1000;
+        DisplayCutout expected = new DisplayCutout(Insets.of(0, 20, 100, 20),
+                ZERO_RECT, ZERO_RECT, new Rect(displayH - 100, 50, displayH - 0, 75), ZERO_RECT,
+                Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_270));
+        DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+                ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+                Insets.of(20, 0, 20, 0));
+        DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_270);
+        assertEquals(expected, rotated);
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot90to180() {
+        int displayW = 500, displayH = 1000;
+        DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+                ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+                Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180));
+        DisplayCutout cutout = new DisplayCutout(Insets.of(100, 20, 0, 20),
+                new Rect(0, displayW - 75, 100, displayW - 50), ZERO_RECT, ZERO_RECT, ZERO_RECT,
+                Insets.of(0, 20, 0, 20));
+        // starting from 90, so the start displayW/H are swapped:
+        DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
+        assertEquals(expected, rotated);
+    }
+
     private static DisplayCutout createCutoutTop() {
         return createCutoutWithInsets(0, 100, 0, 0);
     }
@@ -533,4 +605,11 @@
                 ZERO_RECT,
                 waterfallInsets);
     }
+
+    private static DisplayCutout.CutoutPathParserInfo createParserInfo(
+            @Surface.Rotation int rotation) {
+        return new DisplayCutout.CutoutPathParserInfo(
+                0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
+                rotation, 0f /* scale */);
+    }
 }
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 656e756..b2a4044 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -21,16 +21,25 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.app.Activity;
 import android.app.EmptyActivity;
 import android.app.Instrumentation;
+import android.app.WindowConfiguration;
+import android.content.ComponentCallbacks;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.IBinder;
@@ -43,6 +52,7 @@
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerImpl;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
@@ -79,6 +89,8 @@
     private final WindowContext mWindowContext = createWindowContext();
     private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
 
+    private static final int TIMEOUT_IN_SECONDS = 4;
+
     @Test
     public void testCreateWindowContextWindowManagerAttachClientToken() {
         final WindowManager windowContextWm = WindowManagerImpl
@@ -131,7 +143,7 @@
         });
 
 
-        assertTrue(latch.await(4, TimeUnit.SECONDS));
+        assertTrue(latch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
 
 
         // Verify that the window token of the window context is created after first addView().
@@ -234,7 +246,7 @@
         // Add the parent window
         mInstrumentation.runOnMainSync(() -> wm.addView(parentWindow, params));
 
-        assertTrue(listener.mLatch.await(4, TimeUnit.SECONDS));
+        assertTrue(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
 
         final WindowManager.LayoutParams subWindowAttrs =
                 new WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG);
@@ -251,6 +263,63 @@
                 null /* theme */));
     }
 
+    @Test
+    public void testRegisterComponentCallbacks() {
+        final WindowContext windowContext = createWindowContext();
+        final ConfigurationListener listener = new ConfigurationListener();
+
+        windowContext.registerComponentCallbacks(listener);
+
+        try {
+            final Configuration config = new Configuration();
+            config.windowConfiguration.setWindowingMode(
+                    WindowConfiguration.WINDOWING_MODE_FREEFORM);
+            config.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
+
+            windowContext.dispatchConfigurationChanged(config);
+
+            try {
+                assertWithMessage("Waiting for onConfigurationChanged timeout.")
+                        .that(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)).isTrue();
+            } catch (InterruptedException e) {
+                fail("Waiting for configuration changed failed because of " + e);
+            }
+
+            assertThat(listener.mConfiguration).isEqualTo(config);
+        } finally {
+            windowContext.unregisterComponentCallbacks(listener);
+        }
+    }
+
+    @Test
+    public void testRegisterComponentCallbacksOnWindowContextWrapper() {
+        final WindowContext windowContext = createWindowContext();
+        final Context wrapper = new ContextWrapper(windowContext);
+        final ConfigurationListener listener = new ConfigurationListener();
+
+        wrapper.registerComponentCallbacks(listener);
+
+        try {
+            final Configuration config = new Configuration();
+            config.windowConfiguration.setWindowingMode(
+                    WindowConfiguration.WINDOWING_MODE_FREEFORM);
+            config.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
+
+            windowContext.dispatchConfigurationChanged(config);
+
+            try {
+                assertWithMessage("Waiting for onConfigurationChanged timeout.")
+                        .that(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)).isTrue();
+            } catch (InterruptedException e) {
+                fail("Waiting for configuration changed failed because of " + e);
+            }
+
+            assertThat(listener.mConfiguration).isEqualTo(config);
+        } finally {
+            wrapper.unregisterComponentCallbacks(listener);
+        }
+    }
+
     private WindowContext createWindowContext() {
         return createWindowContext(TYPE_APPLICATION_OVERLAY);
     }
@@ -262,6 +331,20 @@
         return (WindowContext) instContext.createWindowContext(display, type,  null /* options */);
     }
 
+    private static class ConfigurationListener implements ComponentCallbacks {
+        private Configuration mConfiguration;
+        private CountDownLatch mLatch = new CountDownLatch(1);
+
+        @Override
+        public void onConfigurationChanged(@NonNull Configuration newConfig) {
+            mConfiguration = newConfig;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onLowMemory() {}
+    }
+
     private static class AttachStateListener implements View.OnAttachStateChangeListener {
         final CountDownLatch mLatch = new CountDownLatch(1);
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 92fca36..88920c8 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -231,6 +231,18 @@
                       targetSdk="29">
         <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
     </split-permission>
+    <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+                      targetSdk="33">
+        <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+    </split-permission>
+    <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+                      targetSdk="33">
+        <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+    </split-permission>
+    <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+                      targetSdk="33">
+        <new-permission name="android.permission.READ_MEDIA_IMAGE" />
+    </split-permission>
     <split-permission name="android.permission.BLUETOOTH"
                       targetSdk="31">
         <new-permission name="android.permission.BLUETOOTH_SCAN" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index de086df..0e06fac 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -528,6 +528,7 @@
         <!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
+        <permission name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8f73b9a1..1567233 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -355,6 +355,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-1728919185": {
+      "message": "        unrelated invisible sibling %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-1715268616": {
       "message": "Last window, removing starting window %s",
       "level": "VERBOSE",
@@ -451,12 +457,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "-1587921395": {
-      "message": "  Top targets: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "-1585311008": {
       "message": "Bring to front target: %s from %s",
       "level": "DEBUG",
@@ -709,12 +709,6 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
-    "-1375751630": {
-      "message": "  --- Start combine pass ---",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "-1364754753": {
       "message": "Task vanished taskId=%d",
       "level": "VERBOSE",
@@ -1177,12 +1171,6 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
     },
-    "-855366859": {
-      "message": "        merging children in from %s: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "-853404763": {
       "message": "\twallpaper=%s",
       "level": "DEBUG",
@@ -1243,6 +1231,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-779095785": {
+      "message": "        sibling is a participant with mode %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-775004869": {
       "message": "Not a match: %s",
       "level": "DEBUG",
@@ -1555,12 +1549,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
-    "-446752714": {
-      "message": "        SKIP: sibling contains top target %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "-445944810": {
       "message": "finish(%b): mCanceled=%b",
       "level": "DEBUG",
@@ -1729,12 +1717,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
-    "-302335479": {
-      "message": "        remove from topTargets %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "-292790591": {
       "message": "Attempted to set IME policy to a display that does not exist: %d",
       "level": "WARN",
@@ -2077,6 +2059,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "112145970": {
+      "message": "      SKIP: its sibling was rejected",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "114070759": {
       "message": "New wallpaper target: %s prevTarget: %s caller=%s",
       "level": "VERBOSE",
@@ -2413,6 +2401,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "405146734": {
+      "message": "  Final targets: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "416924848": {
       "message": "InsetsSource Control %s for target %s",
       "level": "DEBUG",
@@ -2437,12 +2431,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "430260320": {
-      "message": "        sibling is a top target with mode %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "431715812": {
       "message": "Launch on display check: allow launch any on display",
       "level": "DEBUG",
@@ -2761,12 +2749,6 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
-    "751854538": {
-      "message": "DisplayArea keep clear rects changed name =%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_ORGANIZER",
-      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
-    },
     "765395228": {
       "message": "onAnimationFinished(): controller=%s reorderMode=%d",
       "level": "DEBUG",
@@ -3097,12 +3079,6 @@
       "group": "WM_DEBUG_WALLPAPER",
       "at": "com\/android\/server\/wm\/WallpaperController.java"
     },
-    "1186730970": {
-      "message": "          no common mode yet, so set it",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Transition.java"
-    },
     "1191587912": {
       "message": "Moved rootTask=%s behind rootTask=%s",
       "level": "DEBUG",
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background.xml
new file mode 100644
index 0000000..723963f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="@android:color/system_accent1_100"/>
+    <corners android:radius="12dp"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background_ripple.xml
new file mode 100644
index 0000000..0a3a813
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_background_ripple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<!-- DO NOT SUBMIT - find right color!! -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@android:color/system_accent1_10">
+    <item android:drawable="@drawable/letterbox_education_dismiss_background"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
new file mode 100644
index 0000000..ff57406
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/system_neutral2_400"
+        android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml
new file mode 100644
index 0000000..16dea48
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@android:color/system_neutral2_200">
+    <item android:drawable="@drawable/letterbox_education_ic_expand_more"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
new file mode 100644
index 0000000..cbfcfd0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/letterbox_education_dialog_icon_size"
+        android:height="@dimen/letterbox_education_dialog_icon_size"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:fillType="evenOdd"
+        android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:pathData="M 14 22 H 30 V 26 H 14 V 22 Z" />
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:pathData="M26 16L34 24L26 32V16Z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
new file mode 100644
index 0000000..469eb1e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/letterbox_education_dialog_icon_size"
+        android:height="@dimen/letterbox_education_dialog_icon_size"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:fillType="evenOdd"
+        android:pathData="M22.56 2H26C37.02 2 46 10.98 46 22H42C42 14.44 36.74 8.1 29.7 6.42L31.74 10L28.26 12L22.56 2ZM22 46H25.44L19.74 36L16.26 38L18.3 41.58C11.26 39.9 6 33.56 6 26H2C2 37.02 10.98 46 22 46ZM20.46 12L36 27.52L27.54 36L12 20.48L20.46 12ZM17.64 9.18C18.42 8.4 19.44 8 20.46 8C21.5 8 22.52 8.4 23.3 9.16L38.84 24.7C40.4 26.26 40.4 28.78 38.84 30.34L30.36 38.82C29.58 39.6 28.56 40 27.54 40C26.52 40 25.5 39.6 24.72 38.82L9.18 23.28C7.62 21.72 7.62 19.2 9.18 17.64L17.64 9.18Z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
new file mode 100644
index 0000000..dcb8aed
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/letterbox_education_dialog_icon_size"
+        android:height="@dimen/letterbox_education_dialog_icon_size"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:fillType="evenOdd"
+        android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:pathData="M6 16C6 14.8954 6.89543 14 8 14H21C22.1046 14 23 14.8954 23 16V32C23 33.1046 22.1046 34 21 34H8C6.89543 34 6 33.1046 6 32V16Z" />
+    <path
+        android:fillColor="@color/letterbox_education_text_secondary"
+        android:pathData="M25 16C25 14.8954 25.8954 14 27 14H40C41.1046 14 42 14.8954 42 16V32C42 33.1046 41.1046 34 40 34H27C25.8954 34 25 33.1046 25 32V16Z" />
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
new file mode 100644
index 0000000..cd1d99a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
@@ -0,0 +1,40 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/letterbox_education_dialog_action_width"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/letterbox_education_dialog_action_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginBottom="12dp"/>
+
+    <TextView
+        android:id="@+id/letterbox_education_dialog_action_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:lineSpacingExtra="4sp"
+        android:textAlignment="center"
+        android:textColor="@color/compat_controls_text"
+        android:textSize="14sp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
new file mode 100644
index 0000000..edf737f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
@@ -0,0 +1,98 @@
+<!--
+  ~ Copyright (C) 2022 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.compatui.LetterboxEduDialogLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@color/compat_controls_background"
+    android:gravity="center"
+    android:paddingTop="24dp"
+    android:paddingBottom="32dp"
+    android:paddingHorizontal="32dp">
+
+    <!-- Adding an extra layer to animate the alpha of the background and content separately. -->
+    <LinearLayout
+        android:id="@+id/letterbox_education_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/letterbox_education_icon"
+            android:layout_width="@dimen/letterbox_education_dialog_icon_size"
+            android:layout_height="@dimen/letterbox_education_dialog_icon_size"
+            android:layout_marginBottom="20dp" />
+
+        <TextView
+            android:id="@+id/letterbox_education_dialog_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="@dimen/letterbox_education_dialog_title_max_width"
+            android:lineSpacingExtra="4sp"
+            android:textAlignment="center"
+            android:textColor="@color/compat_controls_text"
+            android:textSize="24sp"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="top"
+            android:orientation="horizontal"
+            android:paddingTop="43dp">
+
+            <com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
+                android:id="@+id/letterbox_education_dialog_screen_rotation_action"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:icon="@drawable/letterbox_education_ic_screen_rotation"/>
+
+            <com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
+                android:id="@+id/letterbox_education_dialog_split_screen_action"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/letterbox_education_dialog_space_between_actions"
+                app:icon="@drawable/letterbox_education_ic_split_screen"
+                app:text="@string/letterbox_education_split_screen_text"/>
+
+            <com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
+                android:id="@+id/letterbox_education_dialog_reposition_action"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layout_marginStart="@dimen/letterbox_education_dialog_space_between_actions"
+                app:icon="@drawable/letterbox_education_ic_reposition"
+                app:text="@string/letterbox_education_reposition_text"/>
+
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/letterbox_education_dialog_dismiss"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginHorizontal="24dp"
+            android:background="@drawable/letterbox_education_dismiss_background_ripple"
+            android:gravity="center"
+            android:text="@string/letterbox_education_got_it"
+            android:textColor="@android:color/system_neutral1_900"
+            android:textAlignment="center"
+            android:contentDescription="@string/letterbox_education_got_it"/>
+
+    </LinearLayout>
+
+</com.android.wm.shell.compatui.LetterboxEduDialogLayout>
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_overlay_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_overlay_layout.xml
new file mode 100644
index 0000000..f4c6d65
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_overlay_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:alpha="0"
+    android:background="@android:color/system_neutral1_900"
+    android:clickable="true">
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml
new file mode 100644
index 0000000..a309d48
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml
@@ -0,0 +1,61 @@
+<!--
+  ~ Copyright (C) 2022 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.compatui.LetterboxEduToastLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@color/compat_controls_background"
+    android:gravity="center"
+    android:paddingVertical="14dp"
+    android:paddingHorizontal="16dp">
+
+    <!-- Adding an extra layer to animate the alpha of the background and content separately. -->
+    <LinearLayout
+        android:id="@+id/letterbox_education_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/letterbox_education_icon"
+            android:layout_width="@dimen/letterbox_education_toast_icon_size"
+            android:layout_height="@dimen/letterbox_education_toast_icon_size"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="@dimen/letterbox_education_toast_text_max_width"
+            android:paddingHorizontal="16dp"
+            android:lineSpacingExtra="5sp"
+            android:text="@string/letterbox_education_toast_title"
+            android:textAlignment="viewStart"
+            android:textColor="@color/compat_controls_text"
+            android:textSize="16sp"
+            android:maxLines="1"
+            android:ellipsize="end"/>
+
+        <ImageButton
+            android:id="@+id/letterbox_education_toast_expand"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/letterbox_education_ic_expand_more_ripple"
+            android:background="@android:color/transparent"
+            android:contentDescription="@string/letterbox_education_expand_button_description"/>
+
+    </LinearLayout>
+
+</com.android.wm.shell.compatui.LetterboxEduToastLayout>
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
new file mode 100644
index 0000000..4aaeef8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <declare-styleable name="LetterboxEduDialogActionLayout">
+        <attr name="icon" format="reference" />
+        <attr name="text" format="string" />
+    </declare-styleable>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index cf596f7..84aec64 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -34,6 +34,9 @@
     <color name="compat_controls_background">@android:color/system_neutral1_800</color>
     <color name="compat_controls_text">@android:color/system_neutral1_50</color>
 
+    <!-- Letterbox Education -->
+    <color name="letterbox_education_text_secondary">@android:color/system_neutral2_200</color>
+
     <!-- GM2 colors -->
     <color name="GM2_grey_200">#E8EAED</color>
     <color name="GM2_grey_700">#5F6368</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1c19a10..ab2c9b1 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -219,6 +219,37 @@
     <!-- The width of the camera compat hint. -->
     <dimen name="camera_compat_hint_width">143dp</dimen>
 
+    <!-- The corner radius of the letterbox education toast. -->
+    <dimen name="letterbox_education_toast_corner_radius">100dp</dimen>
+
+    <!-- The corner radius of the letterbox education dialog. -->
+    <dimen name="letterbox_education_dialog_corner_radius">28dp</dimen>
+
+    <!-- The margin between the letterbox education toast/dialog and the bottom of the task. -->
+    <dimen name="letterbox_education_margin_bottom">16dp</dimen>
+
+    <!-- The size of the icon in the letterbox education toast. -->
+    <dimen name="letterbox_education_toast_icon_size">24dp</dimen>
+
+    <!-- The size of an icon in the letterbox education dialog. -->
+    <dimen name="letterbox_education_dialog_icon_size">48dp</dimen>
+
+    <!-- The width of each action container in the letterbox education dialog -->
+    <dimen name="letterbox_education_dialog_action_width">136dp</dimen>
+
+    <!-- The space between two actions in the letterbox education dialog -->
+    <dimen name="letterbox_education_dialog_space_between_actions">18dp</dimen>
+
+    <!-- The maximum width of the title and subtitle in the letterbox education dialog. -->
+    <dimen name="letterbox_education_dialog_title_max_width">444dp</dimen>
+
+    <!-- The maximum width of the text in the letterbox education toast. -->
+    <dimen name="letterbox_education_toast_text_max_width">398dp</dimen>
+
+    <!-- The distance that the letterbox education dialog will move up during appear/dismiss
+         animation.  -->
+    <dimen name="letterbox_education_dialog_animation_elevation">20dp</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/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index ab0013a..a8a9ed7 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -171,4 +171,28 @@
          compatibility control. [CHAR LIMIT=NONE] -->
     <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
 
+    <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_dialog_title">Get the most out of <xliff:g id="app_name" example="YouTube">%s</xliff:g></string>
+
+    <!-- The title of the letterbox education toast. [CHAR LIMIT=60] -->
+    <string name="letterbox_education_toast_title">Rotate your device for a full-screen view</string>
+
+    <!-- Description of the rotate screen into portrait action. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_screen_rotation_portrait_text">Rotate your screen to portrait</string>
+
+    <!-- Description of the rotate screen into landscape action. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_screen_rotation_landscape_text">Rotate your screen to landscape</string>
+
+    <!-- Description of the put app in split-screen action. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_split_screen_text">Drag in another app to use split screen</string>
+
+    <!-- Description of the reposition app action. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_reposition_text">Double tap to reposition</string>
+
+    <!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
+    <string name="letterbox_education_got_it">Got it</string>
+
+    <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_education_expand_button_description">Expand for more information.</string>
+
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
new file mode 100644
index 0000000..762a037
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.letterboxedu;
+
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Custom layout for Letterbox Education dialog action.
+ */
+// TODO(b/215316431): Add tests
+class LetterboxEduDialogActionLayout extends FrameLayout {
+    private final ImageView mIcon;
+    private final TextView mText;
+
+    LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray styledAttributes =
+                context.getTheme().obtainStyledAttributes(
+                        attrs,
+                        R.styleable.LetterboxEduDialogActionLayout,
+                        /* defStyleAttr= */ 0,
+                        /* defStyleRes= */ 0);
+        int iconId = styledAttributes.getResourceId(
+                R.styleable.LetterboxEduDialogActionLayout_icon, 0);
+        String optionalText = styledAttributes.getString(
+                R.styleable.LetterboxEduDialogActionLayout_text);
+        styledAttributes.recycle();
+
+        View rootView = inflate(getContext(), R.layout.letterbox_education_dialog_action_layout,
+                this);
+
+        mIcon = rootView.findViewById(R.id.letterbox_education_dialog_action_icon);
+        mIcon.setImageResource(iconId);
+        mText = rootView.findViewById(R.id.letterbox_education_dialog_action_text);
+        if (optionalText != null) {
+            mText.setText(optionalText);
+        }
+    }
+
+    void setText(@StringRes int id) {
+        mText.setText(getResources().getString(id));
+    }
+
+    void setIconRotation(float rotation) {
+        mIcon.setRotation(rotation);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
new file mode 100644
index 0000000..662862a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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.letterboxedu;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for Letterbox Education Dialog.
+ */
+// TODO(b/215316431): Add tests
+public class LetterboxEduDialogLayout extends FrameLayout {
+
+    public LetterboxEduDialogLayout(Context context) {
+        this(context, null);
+    }
+
+    public LetterboxEduDialogLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LetterboxEduDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LetterboxEduDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Register a callback for the dismiss button.
+     * @param callback The callback to register
+     */
+    void setDismissOnClickListener(Runnable callback) {
+        findViewById(R.id.letterbox_education_dialog_dismiss).setOnClickListener(
+                view -> callback.run());
+    }
+
+    /**
+     * Updates the layout with the given app info.
+     * @param appIcon The name of the app
+     * @param appIcon The icon of the app
+     */
+    void updateAppInfo(String appName, Drawable appIcon) {
+        ((ImageView) findViewById(R.id.letterbox_education_icon)).setImageDrawable(appIcon);
+        ((TextView) findViewById(R.id.letterbox_education_dialog_title)).setText(
+                getResources().getString(R.string.letterbox_education_dialog_title, appName));
+    }
+
+    /**
+     * Updates the layout according to the given orientation.
+     * @param orientation The orientation of the display
+     */
+    void updateDisplayOrientation(@Orientation int orientation) {
+        boolean isOrientationPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
+        ((LetterboxEduDialogActionLayout) findViewById(
+                R.id.letterbox_education_dialog_screen_rotation_action)).setText(
+                isOrientationPortrait
+                        ? R.string.letterbox_education_screen_rotation_landscape_text
+                        : R.string.letterbox_education_screen_rotation_portrait_text);
+
+        if (isOrientationPortrait) {
+            ((LetterboxEduDialogActionLayout) findViewById(
+                    R.id.letterbox_education_dialog_split_screen_action)).setIconRotation(90f);
+        }
+
+        findViewById(R.id.letterbox_education_dialog_reposition_action).setVisibility(
+                isOrientationPortrait ? View.GONE : View.VISIBLE);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java
new file mode 100644
index 0000000..e7f592d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.letterboxedu;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for the Letterbox Education Toast.
+ */
+// TODO(b/215316431): Add tests
+public class LetterboxEduToastLayout extends FrameLayout {
+
+    public LetterboxEduToastLayout(Context context) {
+        this(context, null);
+    }
+
+    public LetterboxEduToastLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Register a callback for the dismiss button.
+     * @param callback The callback to register
+     */
+    void setExpandOnClickListener(Runnable callback) {
+        findViewById(R.id.letterbox_education_toast_expand).setOnClickListener(
+                view -> callback.run());
+    }
+
+    /**
+     * Updates the layout with the given app info.
+     * @param appName The name of the app
+     * @param appIcon The icon of the app
+     */
+    void updateAppInfo(String appName, Drawable appIcon) {
+        ImageView icon = findViewById(R.id.letterbox_education_icon);
+        icon.setContentDescription(appName);
+        icon.setImageDrawable(appIcon);
+    }
+}
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 6921448..3e6dc82 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
@@ -137,6 +137,9 @@
     private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
 
     private StageCoordinator mStageCoordinator;
+    // Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
+    // outside the bounds of the roots by being reparented into a higher level fullscreen container
+    private SurfaceControl mSplitTasksContainerLayer;
 
     public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
@@ -378,20 +381,24 @@
     RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
         if (ENABLE_SHELL_TRANSITIONS || apps.length < 2) return null;
         // TODO(b/206487881): Integrate this with shell transition.
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        if (mSplitTasksContainerLayer != null) {
+            // Remove the previous layer before recreating
+            transaction.remove(mSplitTasksContainerLayer);
+        }
         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
                 .setContainerLayer()
                 .setName("RecentsAnimationSplitTasks")
                 .setHidden(false)
                 .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
         mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
-        SurfaceControl sc = builder.build();
-        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        mSplitTasksContainerLayer = builder.build();
 
         // Ensure that we order these in the parent in the right z-order as their previous order
         Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
         int layer = 1;
         for (RemoteAnimationTarget appTarget : apps) {
-            transaction.reparent(appTarget.leash, sc);
+            transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
             transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
                     appTarget.screenSpaceBounds.top);
             transaction.setLayer(appTarget.leash, layer++);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 4ecc0b6..1bef552e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -126,9 +126,6 @@
      */
     private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
 
-    //tmp vars for unused relayout params
-    private static final Point TMP_SURFACE_SIZE = new Point();
-
     private final Window mWindow;
     private final Runnable mClearWindowHandler;
     private final ShellExecutor mSplashScreenExecutor;
@@ -244,9 +241,9 @@
         window.setOuter(snapshotSurface);
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
-            session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+            session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0,
                     tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
-                    tmpControls, TMP_SURFACE_SIZE);
+                    tmpControls);
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         } catch (RemoteException e) {
             snapshotSurface.clearWindowSynced();
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 553b08f..7c57bd5 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -78,7 +78,9 @@
 
 static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
                                                 const shaders::LinearEffect& linearEffect,
-                                                float maxDisplayLuminance, float maxLuminance) {
+                                                float maxDisplayLuminance,
+                                                float currentDisplayLuminanceNits,
+                                                float maxLuminance) {
     auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
     auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
     if (!runtimeEffect) {
@@ -89,8 +91,8 @@
 
     effectBuilder.child("child") = std::move(shader);
 
-    const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, mat4(),
-                                                             maxDisplayLuminance, maxLuminance);
+    const auto uniforms = shaders::buildLinearEffectUniforms(
+            linearEffect, mat4(), maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance);
 
     for (const auto& uniform : uniforms) {
         effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
@@ -201,7 +203,9 @@
             auto shader = layerImage->makeShader(sampling,
                                                  SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
             constexpr float kMaxDisplayBrightess = 1000.f;
+            constexpr float kCurrentDisplayBrightness = 500.f;
             shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
+                                              kCurrentDisplayBrightness,
                                               layer->getMaxLuminanceNits());
             paint.setShader(shader);
             canvas->drawRect(skiaDestRect, paint);
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index d6e203c..587222a 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -29,7 +29,6 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -180,13 +179,9 @@
     private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1;
     private static final double IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR = 1D / 6D;
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
-            + "LocationManager} methods to provide the provider explicitly.")
-    @Nullable private String mProvider;
+    private @Nullable String mProvider;
     private @Quality int mQuality;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
-            + "LocationRequest} instead.")
-    private long mInterval;
+    private long mIntervalMillis;
     private long mMinUpdateIntervalMillis;
     private long mExpireAtRealtimeMillis;
     private long mDurationMillis;
@@ -195,7 +190,7 @@
     private final long mMaxUpdateDelayMillis;
     private boolean mHideFromAppOps;
     private final boolean mAdasGnssBypass;
-    private boolean mLocationSettingsIgnored;
+    private boolean mBypass;
     private boolean mLowPower;
     private @Nullable WorkSource mWorkSource;
 
@@ -208,9 +203,7 @@
     @NonNull
     public static LocationRequest create() {
         // 60 minutes is the default legacy interval
-        return new LocationRequest.Builder(60 * 60 * 1000)
-                .setQuality(QUALITY_LOW_POWER)
-                .build();
+        return new LocationRequest.Builder(60 * 60 * 1000).build();
     }
 
     /**
@@ -239,7 +232,7 @@
         } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
             quality = QUALITY_HIGH_ACCURACY;
         } else {
-            quality = POWER_LOW;
+            quality = QUALITY_BALANCED_POWER_ACCURACY;
         }
 
         return new LocationRequest.Builder(intervalMillis)
@@ -291,11 +284,11 @@
             long maxUpdateDelayMillis,
             boolean hiddenFromAppOps,
             boolean adasGnssBypass,
-            boolean locationSettingsIgnored,
+            boolean bypass,
             boolean lowPower,
             WorkSource workSource) {
         mProvider = provider;
-        mInterval = intervalMillis;
+        mIntervalMillis = intervalMillis;
         mQuality = quality;
         mMinUpdateIntervalMillis = minUpdateIntervalMillis;
         mExpireAtRealtimeMillis = expireAtRealtimeMillis;
@@ -305,7 +298,7 @@
         mMaxUpdateDelayMillis = maxUpdateDelayMillis;
         mHideFromAppOps = hiddenFromAppOps;
         mAdasGnssBypass = adasGnssBypass;
-        mLocationSettingsIgnored = locationSettingsIgnored;
+        mBypass = bypass;
         mLowPower = lowPower;
         mWorkSource = Objects.requireNonNull(workSource);
     }
@@ -354,7 +347,7 @@
                 mQuality = QUALITY_LOW_POWER;
                 break;
             case POWER_NONE:
-                mInterval = PASSIVE_INTERVAL;
+                mIntervalMillis = PASSIVE_INTERVAL;
                 break;
             default:
                 throw new IllegalArgumentException("invalid quality: " + quality);
@@ -388,9 +381,9 @@
             millis = Long.MAX_VALUE - 1;
         }
 
-        mInterval = millis;
-        if (mMinUpdateIntervalMillis > mInterval) {
-            mMinUpdateIntervalMillis = mInterval;
+        mIntervalMillis = millis;
+        if (mMinUpdateIntervalMillis > mIntervalMillis) {
+            mMinUpdateIntervalMillis = mIntervalMillis;
         }
         return this;
     }
@@ -418,7 +411,7 @@
      * @return the desired interval of location updates
      */
     public @IntRange(from = 0) long getIntervalMillis() {
-        return mInterval;
+        return mIntervalMillis;
     }
 
     /**
@@ -556,11 +549,11 @@
      */
     public @IntRange(from = 0) long getMinUpdateIntervalMillis() {
         if (mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
-            return (long) (mInterval * IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
+            return (long) (mIntervalMillis * IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
         } else {
             // the min is only necessary in case someone use a deprecated function to mess with the
             // interval or min update interval
-            return min(mMinUpdateIntervalMillis, mInterval);
+            return min(mMinUpdateIntervalMillis, mIntervalMillis);
         }
     }
 
@@ -673,7 +666,7 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
-        mLocationSettingsIgnored = locationSettingsIgnored;
+        mBypass = locationSettingsIgnored;
         return this;
     }
 
@@ -687,7 +680,7 @@
      */
     @SystemApi
     public boolean isLocationSettingsIgnored() {
-        return mLocationSettingsIgnored;
+        return mBypass;
     }
 
     /**
@@ -696,7 +689,7 @@
      * @hide
      */
     public boolean isBypass() {
-        return mAdasGnssBypass || mLocationSettingsIgnored;
+        return mAdasGnssBypass || mBypass;
     }
 
     /**
@@ -796,7 +789,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeString(mProvider);
-        parcel.writeLong(mInterval);
+        parcel.writeLong(mIntervalMillis);
         parcel.writeInt(mQuality);
         parcel.writeLong(mExpireAtRealtimeMillis);
         parcel.writeLong(mDurationMillis);
@@ -806,7 +799,7 @@
         parcel.writeLong(mMaxUpdateDelayMillis);
         parcel.writeBoolean(mHideFromAppOps);
         parcel.writeBoolean(mAdasGnssBypass);
-        parcel.writeBoolean(mLocationSettingsIgnored);
+        parcel.writeBoolean(mBypass);
         parcel.writeBoolean(mLowPower);
         parcel.writeTypedObject(mWorkSource, 0);
     }
@@ -821,7 +814,7 @@
         }
 
         LocationRequest that = (LocationRequest) o;
-        return mInterval == that.mInterval
+        return mIntervalMillis == that.mIntervalMillis
                 && mQuality == that.mQuality
                 && mExpireAtRealtimeMillis == that.mExpireAtRealtimeMillis
                 && mDurationMillis == that.mDurationMillis
@@ -831,7 +824,7 @@
                 && mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
                 && mHideFromAppOps == that.mHideFromAppOps
                 && mAdasGnssBypass == that.mAdasGnssBypass
-                && mLocationSettingsIgnored == that.mLocationSettingsIgnored
+                && mBypass == that.mBypass
                 && mLowPower == that.mLowPower
                 && Objects.equals(mProvider, that.mProvider)
                 && Objects.equals(mWorkSource, that.mWorkSource);
@@ -839,7 +832,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mProvider, mInterval, mWorkSource);
+        return Objects.hash(mProvider, mIntervalMillis, mWorkSource);
     }
 
     @NonNull
@@ -850,9 +843,9 @@
         if (mProvider != null) {
             s.append(mProvider).append(" ");
         }
-        if (mInterval != PASSIVE_INTERVAL) {
+        if (mIntervalMillis != PASSIVE_INTERVAL) {
             s.append("@");
-            TimeUtils.formatDuration(mInterval, s);
+            TimeUtils.formatDuration(mIntervalMillis, s);
 
             switch (mQuality) {
                 case QUALITY_HIGH_ACCURACY:
@@ -879,14 +872,14 @@
             s.append(", maxUpdates=").append(mMaxUpdates);
         }
         if (mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL
-                && mMinUpdateIntervalMillis < mInterval) {
+                && mMinUpdateIntervalMillis < mIntervalMillis) {
             s.append(", minUpdateInterval=");
             TimeUtils.formatDuration(mMinUpdateIntervalMillis, s);
         }
         if (mMinUpdateDistanceMeters > 0.0) {
             s.append(", minUpdateDistance=").append(mMinUpdateDistanceMeters);
         }
-        if (mMaxUpdateDelayMillis / 2 > mInterval) {
+        if (mMaxUpdateDelayMillis / 2 > mIntervalMillis) {
             s.append(", maxUpdateDelay=");
             TimeUtils.formatDuration(mMaxUpdateDelayMillis, s);
         }
@@ -899,8 +892,8 @@
         if (mAdasGnssBypass) {
             s.append(", adasGnssBypass");
         }
-        if (mLocationSettingsIgnored) {
-            s.append(", settingsBypass");
+        if (mBypass) {
+            s.append(", bypass");
         }
         if (mWorkSource != null && !mWorkSource.isEmpty()) {
             s.append(", ").append(mWorkSource);
@@ -923,7 +916,7 @@
         private long mMaxUpdateDelayMillis;
         private boolean mHiddenFromAppOps;
         private boolean mAdasGnssBypass;
-        private boolean mLocationSettingsIgnored;
+        private boolean mBypass;
         private boolean mLowPower;
         @Nullable private WorkSource mWorkSource;
 
@@ -943,7 +936,7 @@
             mMaxUpdateDelayMillis = 0;
             mHiddenFromAppOps = false;
             mAdasGnssBypass = false;
-            mLocationSettingsIgnored = false;
+            mBypass = false;
             mLowPower = false;
             mWorkSource = null;
         }
@@ -952,7 +945,7 @@
          * Creates a new Builder with all parameters copied from the given location request.
          */
         public Builder(@NonNull LocationRequest locationRequest) {
-            mIntervalMillis = locationRequest.mInterval;
+            mIntervalMillis = locationRequest.mIntervalMillis;
             mQuality = locationRequest.mQuality;
             mDurationMillis = locationRequest.mDurationMillis;
             mMaxUpdates = locationRequest.mMaxUpdates;
@@ -961,7 +954,7 @@
             mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
             mHiddenFromAppOps = locationRequest.mHideFromAppOps;
             mAdasGnssBypass = locationRequest.mAdasGnssBypass;
-            mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
+            mBypass = locationRequest.mBypass;
             mLowPower = locationRequest.mLowPower;
             mWorkSource = locationRequest.mWorkSource;
 
@@ -1160,14 +1153,15 @@
         @SystemApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
-            mLocationSettingsIgnored = locationSettingsIgnored;
+            mBypass = locationSettingsIgnored;
             return this;
         }
 
         /**
          * It set to true, indicates that extreme trade-offs should be made if possible to save
          * power for this request. This usually involves specialized hardware modes which can
-         * greatly affect the quality of locations. Defaults to false.
+         * greatly affect the quality of locations. Not all devices may support this. Defaults to
+         * false.
          *
          * <p>Permissions enforcement occurs when resulting location request is actually used, not
          * when this method is invoked.
@@ -1227,7 +1221,7 @@
                     mMaxUpdateDelayMillis,
                     mHiddenFromAppOps,
                     mAdasGnssBypass,
-                    mLocationSettingsIgnored,
+                    mBypass,
                     mLowPower,
                     new WorkSource(mWorkSource));
         }
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
index 71b1634..8e80a62 100644
--- a/media/java/android/media/tv/AitInfo.java
+++ b/media/java/android/media/tv/AitInfo.java
@@ -23,7 +23,6 @@
 
 /**
  * AIT (Application Information Table) info.
- * @hide
  */
 public final class AitInfo implements Parcelable {
     static final String TAG = "AitInfo";
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 9bc7367..1a9cab0 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -592,7 +592,11 @@
             });
         }
 
-        /** @hide */
+        /**
+         * Informs the application that this session has been tuned to the given channel.
+         *
+         * @param channelUri The URI of the tuned channel.
+         */
         public void notifyTuned(@NonNull Uri channelUri) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
@@ -947,12 +951,11 @@
         /**
          * Informs the app that the AIT (Application Information Table) is updated.
          *
-         * <p>This method should also be call when
+         * <p>This method should also be called when
          * {@link #onSetInteractiveAppNotificationEnabled(boolean)} is called to send the first AIT
          * info.
          *
          * @see #onSetInteractiveAppNotificationEnabled(boolean)
-         * @hide
          */
         public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -1214,7 +1217,6 @@
          *
          * @see TvView#setInteractiveAppNotificationEnabled(boolean)
          * @see Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
-         * @hide
          */
         public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
         }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index d2086c5..6c25a70 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -493,7 +493,6 @@
      *
      * @see TvInputService.Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
      * @see android.media.tv.interactive.TvInteractiveAppView#setTvView(TvView)
-     * @hide
      */
     public void setInteractiveAppNotificationEnabled(boolean enabled) {
         if (mSession != null) {
@@ -1074,7 +1073,6 @@
          * This is called when the AIT (Application Information Table) info has been updated.
          *
          * @param aitInfo The current AIT info.
-         * @hide
          */
         public void onAitInfoUpdated(@NonNull String inputId, @NonNull AitInfo aitInfo) {
         }
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 39be501..c5dfaa2 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -57,7 +57,7 @@
 
 /**
  * Central system API to the overall TV interactive application framework (TIAF) architecture, which
- * arbitrates interaction between applications and interactive apps.
+ * arbitrates interaction between Android applications and TV interactive apps.
  */
 @SystemService(Context.TV_INTERACTIVE_APP_SERVICE)
 public final class TvInteractiveAppManager {
@@ -75,22 +75,22 @@
 
     /**
      * Unrealized state of interactive app service.
-     * @hide
      */
     public static final int SERVICE_STATE_UNREALIZED = 1;
     /**
      * Preparing state of interactive app service.
-     * @hide
      */
     public static final int SERVICE_STATE_PREPARING = 2;
     /**
      * Ready state of interactive app service.
-     * @hide
+     *
+     * <p>In this state, the interactive app service is ready, and interactive apps can be started.
+     *
+     * @see TvInteractiveAppView#startInteractiveApp()
      */
     public static final int SERVICE_STATE_READY = 3;
     /**
      * Error state of interactive app service.
-     * @hide
      */
     public static final int SERVICE_STATE_ERROR = 4;
 
@@ -105,17 +105,14 @@
 
     /**
      * Stopped (or not started) state of interactive application.
-     * @hide
      */
     public static final int INTERACTIVE_APP_STATE_STOPPED = 1;
     /**
      * Running state of interactive application.
-     * @hide
      */
     public static final int INTERACTIVE_APP_STATE_RUNNING = 2;
     /**
      * Error state of interactive application.
-     * @hide
      */
     public static final int INTERACTIVE_APP_STATE_ERROR = 3;
 
@@ -136,46 +133,37 @@
 
     /**
      * No error.
-     * @hide
      */
     public static final int ERROR_NONE = 0;
     /**
      * Unknown error code.
-     * @hide
      */
     public static final int ERROR_UNKNOWN = 1;
     /**
      * Error code for an unsupported channel.
-     * @hide
      */
     public static final int ERROR_NOT_SUPPORTED = 2;
     /**
      * Error code for weak signal.
-     * @hide
      */
     public static final int ERROR_WEAK_SIGNAL = 3;
     /**
      * Error code when resource (e.g. tuner) is unavailable.
-     * @hide
      */
     public static final int ERROR_RESOURCE_UNAVAILABLE = 4;
     /**
      * Error code for blocked contents.
-     * @hide
      */
     public static final int ERROR_BLOCKED = 5;
     /**
      * Error code when the key or module is missing for the encrypted channel.
-     * @hide
      */
     public static final int ERROR_ENCRYPTED = 6;
     /**
      * Error code when the current channel is an unknown channel.
-     * @hide
      */
     public static final int ERROR_UNKNOWN_CHANNEL = 7;
 
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = false, prefix = "TELETEXT_APP_STATE_", value = {
@@ -561,7 +549,6 @@
 
     /**
      * Callback used to monitor status of the TV Interactive App.
-     * @hide
      */
     public abstract static class TvInteractiveAppCallback {
         /**
@@ -796,7 +783,6 @@
      *
      * @param callback A callback used to monitor status of the TV Interactive App services.
      * @param executor A {@link Executor} that the status change will be delivered to.
-     * @hide
      */
     public void registerCallback(
             @NonNull TvInteractiveAppCallback callback,
@@ -812,7 +798,6 @@
      * Unregisters the existing {@link TvInteractiveAppCallback}.
      *
      * @param callback The existing callback to remove.
-     * @hide
      */
     public void unregisterCallback(@NonNull final TvInteractiveAppCallback callback) {
         Preconditions.checkNotNull(callback);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index d599d0a..afa1ff7 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -34,6 +34,7 @@
 import android.media.tv.TvContentRating;
 import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
+import android.media.tv.TvView;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -65,7 +66,8 @@
 import java.util.List;
 
 /**
- * The TvInteractiveAppService class represents a TV interactive applications RTE.
+ * A TV interactive application service is a service that provides runtime environment and runs TV
+ * interactive applications.
  */
 public abstract class TvInteractiveAppService extends Service {
     private static final boolean DEBUG = false;
@@ -201,10 +203,8 @@
 
     /**
      * Prepares TV Interactive App service for the given type.
-     * @hide
      */
-    public void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type) {
-    }
+    public abstract void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type);
 
     /**
      * Registers App link info.
@@ -239,14 +239,11 @@
      *
      * @param iAppServiceId The ID of the TV Interactive App associated with the session.
      * @param type The type of the TV Interactive App associated with the session.
-     * @hide
      */
     @Nullable
-    public Session onCreateSession(
+    public abstract Session onCreateSession(
             @NonNull String iAppServiceId,
-            @TvInteractiveAppInfo.InteractiveAppType int type) {
-        return null;
-    }
+            @TvInteractiveAppInfo.InteractiveAppType int type);
 
     /**
      * Notifies the system when the state of the interactive app RTE has been changed.
@@ -256,7 +253,6 @@
      * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
      *              used when the state is not
      *              {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
-     * @hide
      */
     public final void notifyStateChanged(
             @TvInteractiveAppInfo.InteractiveAppType int type,
@@ -272,7 +268,12 @@
 
     /**
      * Base class for derived classes to implement to provide a TV interactive app session.
-     * @hide
+     *
+     * <p>A session is associated with a {@link TvInteractiveAppView} instance and handles
+     * corresponding communications. It also handles the communications with
+     * {@link android.media.tv.TvInputService.Session} if connected.
+     *
+     * @see TvInteractiveAppView#setTvView(TvView)
      */
     public abstract static class Session implements KeyEvent.Callback {
         private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
@@ -359,8 +360,11 @@
         /**
          * Creates broadcast-independent(BI) interactive application.
          *
+         * <p>The implementation should call {@link #notifyBiInteractiveAppCreated(Uri, String)},
+         * no matter if it's created successfully or not.
+         *
+         * @see #notifyBiInteractiveAppCreated(Uri, String)
          * @see #onDestroyBiInteractiveApp(String)
-         * @hide
          */
         public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
         }
@@ -370,10 +374,9 @@
          * Destroys broadcast-independent(BI) interactive application.
          *
          * @param biIAppId the BI interactive app ID from
-         *        {@link #createBiInteractiveApp(Uri, Bundle)}
+         *                 {@link #onCreateBiInteractiveApp(Uri, Bundle)}}
          *
          * @see #onCreateBiInteractiveApp(Uri, Bundle)
-         * @hide
          */
         public void onDestroyBiInteractiveApp(@NonNull String biIAppId) {
         }
@@ -482,7 +485,8 @@
 
         /**
          * Called when the corresponding TV input tuned to a channel.
-         * @hide
+         *
+         * @param channelUri The tuned channel URI.
          */
         public void onTuned(@NonNull Uri channelUri) {
         }
@@ -1046,11 +1050,14 @@
 
         /**
          * Notifies the broadcast-independent(BI) interactive application has been created.
+         *
          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
-         *                 app.
-         * @hide
+         *                 app. {@code null} if it's not created successfully.
+         *
+         * @see #onCreateBiInteractiveApp(Uri, Bundle)
          */
-        public final void notifyBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) {
+        public final void notifyBiInteractiveAppCreated(
+                @NonNull Uri biIAppUri, @Nullable String biIAppId) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 12e2199..2922bae 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -387,7 +387,6 @@
      *                      {@link TvInteractiveAppInfo#getId()}.
      *
      * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
-     * @hide
      */
     public void prepareInteractiveApp(
             @NonNull String iAppServiceId,
@@ -416,7 +415,6 @@
 
     /**
      * Stops the interactive application.
-     * @hide
      */
     public void stopInteractiveApp() {
         if (DEBUG) {
@@ -524,8 +522,10 @@
     /**
      * Creates broadcast-independent(BI) interactive application.
      *
-     * @see #destroyBiInteractiveApp(String)
-     * @hide
+     * <p>{@link TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)} will be
+     * called for the result.
+     *
+     * @see TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)
      */
     public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
         if (DEBUG) {
@@ -542,7 +542,6 @@
      * @param biIAppId the BI interactive app ID from {@link #createBiInteractiveApp(Uri, Bundle)}
      *
      * @see #createBiInteractiveApp(Uri, Bundle)
-     * @hide
      */
     public void destroyBiInteractiveApp(@NonNull String biIAppId) {
         if (DEBUG) {
@@ -564,7 +563,6 @@
      *
      * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
      * @return The result of the operation.
-     * @hide
      */
     public int setTvView(@Nullable TvView tvView) {
         if (tvView == null) {
@@ -623,14 +621,13 @@
         }
 
         /**
-         * This is called when the session state is changed.
+         * This is called when the state of corresponding interactive app is changed.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param state the current state.
          * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE}
          *              is used when the state is not
          *              {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
-         * @hide
          */
         public void onStateChanged(
                 @NonNull String iAppServiceId,
@@ -643,10 +640,12 @@
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param biIAppUri URI associated this BI interactive app. This is the same URI in
-         *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
+         *                  {@link #createBiInteractiveApp(Uri, Bundle)}
          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
-         *                 app.
-         * @hide
+         *                 app. {@code null} if it's not created successfully.
+         *
+         * @see #createBiInteractiveApp(Uri, Bundle)
+         * @see #destroyBiInteractiveApp(String)
          */
         public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
                 @Nullable String biIAppId) {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 3157375..be114d4 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -47,6 +47,7 @@
 import android.media.tv.tuner.frontend.FrontendSettings;
 import android.media.tv.tuner.frontend.FrontendStatus;
 import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
+import android.media.tv.tuner.frontend.FrontendStatusReadiness;
 import android.media.tv.tuner.frontend.OnTuneEventListener;
 import android.media.tv.tuner.frontend.ScanCallback;
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
@@ -61,9 +62,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.util.Log;
-
 import com.android.internal.util.FrameworkStatsLog;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -1005,6 +1004,7 @@
     private native int nativeRemoveOutputPid(int pid);
     private native Lnb nativeOpenLnbByHandle(int handle);
     private native Lnb nativeOpenLnbByName(String name);
+    private native FrontendStatusReadiness[] nativeGetFrontendStatusReadiness(int[] statusTypes);
 
     private native Descrambler nativeOpenDescramblerByHandle(int handle);
     private native int nativeOpenDemuxByhandle(int handle);
@@ -1595,6 +1595,38 @@
     }
 
     /**
+     * Gets Frontend Status Readiness statuses for given status types.
+     *
+     * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported versions would cause
+     * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     *
+     * @param statusTypes an array of status types.
+     *
+     * @return an array of current readiness states. {@code null} if the operation failed or
+     *         unsupported versions.
+     * @throws IllegalStateException if there is no active frontend currently.
+     */
+    @Nullable
+    @SuppressLint("ArrayReturn")
+    @SuppressWarnings("NullableCollection")
+    public FrontendStatusReadiness[] getFrontendStatusReadiness(
+            @NonNull @FrontendStatusType int[] statusTypes) {
+        mFrontendLock.lock();
+        try {
+            if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
+                return null;
+            }
+            if (mFrontend == null) {
+                throw new IllegalStateException("frontend is not initialized");
+            }
+            return nativeGetFrontendStatusReadiness(statusTypes);
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
      * Gets the currently initialized and activated frontend information. To get all the available
      * frontend info on the device, use {@link getAvailableFrontendInfos()}.
      *
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
new file mode 100644
index 0000000..52527b3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class contains the Frontend Status Readiness of a given type.
+ *
+ * @hide
+ */
+@SystemApi
+public class FrontendStatusReadiness {
+    /** @hide */
+    @IntDef({FRONTEND_STATUS_READINESS_UNDEFINED, FRONTEND_STATUS_READINESS_UNAVAILABLE,
+            FRONTEND_STATUS_READINESS_UNSTABLE, FRONTEND_STATUS_READINESS_STABLE,
+            FRONTEND_STATUS_READINESS_UNSUPPORTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Readiness {}
+
+    /**
+     * The FrontendStatus readiness status for the given FrontendStatusType is undefined.
+     */
+    public static final int FRONTEND_STATUS_READINESS_UNDEFINED =
+            android.hardware.tv.tuner.FrontendStatusReadiness.UNDEFINED;
+
+    /**
+     * The FrontendStatus for the given FrontendStatusType is currently unavailable.
+     */
+    public static final int FRONTEND_STATUS_READINESS_UNAVAILABLE =
+            android.hardware.tv.tuner.FrontendStatusReadiness.UNAVAILABLE;
+
+    /**
+     * The FrontendStatus for the given FrontendStatusType is ready to read, but it’s unstable.
+     */
+    public static final int FRONTEND_STATUS_READINESS_UNSTABLE =
+            android.hardware.tv.tuner.FrontendStatusReadiness.UNSTABLE;
+
+    /**
+     * The FrontendStatus for the given FrontendStatusType is ready to read, and it’s stable.
+     */
+    public static final int FRONTEND_STATUS_READINESS_STABLE =
+            android.hardware.tv.tuner.FrontendStatusReadiness.STABLE;
+
+    /**
+     * The FrontendStatus for the given FrontendStatusType is not supported.
+     */
+    public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED =
+            android.hardware.tv.tuner.FrontendStatusReadiness.UNSUPPORTED;
+
+    @FrontendStatusType private int mFrontendStatusType;
+    @Readiness private int mStatusReadiness;
+
+    private FrontendStatusReadiness(int type, int readiness) {
+        mFrontendStatusType = type;
+        mStatusReadiness = readiness;
+    }
+
+    /**
+     * Gets the frontend status type.
+     */
+    @FrontendStatusType
+    public int getStatusType() {
+        return mFrontendStatusType;
+    }
+    /**
+     * Gets the frontend status readiness.
+     */
+    @Readiness
+    public int getStatusReadiness() {
+        return mStatusReadiness;
+    }
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 41f3a678..68dd8d0 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1631,6 +1631,36 @@
     return (jint)mFeClient->removeOutputPid(pid);
 }
 
+jobjectArray JTuner::getFrontendStatusReadiness(jintArray types) {
+    if (mFeClient == nullptr) {
+        ALOGE("frontend is not initialized");
+        return nullptr;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jsize size = env->GetArrayLength(types);
+    jint intTypes[size];
+    env->GetIntArrayRegion(types, 0, size, intTypes);
+    std::vector<FrontendStatusType> v;
+    for (int i = 0; i < size; i++) {
+        v.push_back(static_cast<FrontendStatusType>(intTypes[i]));
+    }
+
+    vector<FrontendStatusReadiness> readiness = mFeClient->getStatusReadiness(v);
+    if (readiness.size() < size) {
+        return nullptr;
+    }
+
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatusReadiness");
+    jmethodID init = env->GetMethodID(clazz, "<init>", "(II)V");
+    jobjectArray valObj = env->NewObjectArray(size, clazz, nullptr);
+    for (int i = 0; i < size; i++) {
+        jobject readinessObj = env->NewObject(clazz, init, intTypes[i], readiness[i]);
+        env->SetObjectArrayElement(valObj, i, readinessObj);
+    }
+    return valObj;
+}
+
 jobject JTuner::openLnbByHandle(int handle) {
     if (mTunerClient == nullptr) {
         return nullptr;
@@ -4389,6 +4419,12 @@
     return tuner->removeOutputPid(pid);
 }
 
+static jobjectArray android_media_tv_Tuner_get_frontend_status_readiness(JNIEnv *env, jobject thiz,
+                                                                         jintArray types) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->getFrontendStatusReadiness(types);
+}
+
 static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->closeFrontend();
@@ -4710,6 +4746,9 @@
             (void *)android_media_tv_Tuner_get_maximum_frontends },
     { "nativeRemoveOutputPid", "(I)I",
             (void *)android_media_tv_Tuner_remove_output_pid },
+    { "nativeGetFrontendStatusReadiness",
+            "([I)[Landroid/media/tv/tuner/frontend/FrontendStatusReadiness;",
+            (void *)android_media_tv_Tuner_get_frontend_status_readiness },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index e9475dc..03e7fa9 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -206,6 +206,7 @@
     jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
     int32_t getMaxNumberOfFrontends(int32_t frontendType);
     jint removeOutputPid(int32_t pid);
+    jobjectArray getFrontendStatusReadiness(jintArray types);
 
     jweak getObject();
 
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index bea0342..c6337ec 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -152,6 +152,16 @@
     return Result::INVALID_STATE;
 }
 
+vector<FrontendStatusReadiness> FrontendClient::getStatusReadiness(
+        const std::vector<FrontendStatusType>& statusTypes) {
+    vector<FrontendStatusReadiness> readiness;
+    if (mTunerFrontend != nullptr) {
+        mTunerFrontend->getFrontendStatusReadiness(statusTypes, &readiness);
+    }
+
+    return readiness;
+}
+
 shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
     return mTunerFrontend;
 }
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index c6838c8..85f6d56 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -35,6 +35,7 @@
 using ::aidl::android::hardware::tv::tuner::FrontendScanType;
 using ::aidl::android::hardware::tv::tuner::FrontendSettings;
 using ::aidl::android::hardware::tv::tuner::FrontendStatus;
+using ::aidl::android::hardware::tv::tuner::FrontendStatusReadiness;
 using ::aidl::android::hardware::tv::tuner::FrontendStatusType;
 using ::aidl::android::hardware::tv::tuner::FrontendType;
 using ::aidl::android::hardware::tv::tuner::Result;
@@ -125,6 +126,12 @@
      */
     Result removeOutputPid(int32_t pid);
 
+    /**
+     * Gets Frontend Status Readiness statuses for given status types.
+     */
+    vector<FrontendStatusReadiness> getStatusReadiness(
+            const std::vector<FrontendStatusType>& types);
+
     int32_t getId();
 
     shared_ptr<ITunerFrontend> getAidlFrontend();
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 453e8e6..28f930f 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -729,7 +729,8 @@
      * {@link #unregisterUsageCallback} is called.
      *
      * @param template Template used to match networks. See {@link NetworkTemplate}.
-     * @param thresholdBytes Threshold in bytes to be notified on.
+     * @param thresholdBytes Threshold in bytes to be notified on. The provided value that lower
+     *                       than 2MiB will be clamped for non-privileged callers.
      * @param executor The executor on which callback will be invoked. The provided {@link Executor}
      *                 must run callback sequentially, otherwise the order of callbacks cannot be
      *                 guaranteed.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 58ca21f..735c44d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.IFACE_ALL;
@@ -34,6 +35,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStatsHistory.Entry;
 import android.os.Binder;
 import android.service.NetworkStatsCollectionKeyProto;
 import android.service.NetworkStatsCollectionProto;
@@ -71,6 +74,8 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -80,7 +85,7 @@
  *
  * @hide
  */
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
 public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
     private static final String TAG = NetworkStatsCollection.class.getSimpleName();
     /** File header magic number: "ANET" */
@@ -810,6 +815,71 @@
     }
 
     /**
+     * Get the all historical stats of the collection {@link NetworkStatsCollection}.
+     *
+     * @return All {@link NetworkStatsHistory} in this collection.
+     */
+    @NonNull
+    public Map<Key, NetworkStatsHistory> getEntries() {
+        return new ArrayMap(mStats);
+    }
+
+    /**
+     * Builder class for {@link NetworkStatsCollection}.
+     */
+    public static final class Builder {
+        private final long mBucketDuration;
+        private final ArrayMap<Key, NetworkStatsHistory> mEntries = new ArrayMap<>();
+
+        /**
+         * Creates a new Builder with given bucket duration.
+         *
+         * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+         */
+        public Builder(long bucketDuration) {
+            mBucketDuration = bucketDuration;
+        }
+
+        /**
+         * Add association of the history with the specified key in this map.
+         *
+         * @param key The object used to identify a network, see {@link Key}.
+         * @param history {@link NetworkStatsHistory} instance associated to the given {@link Key}.
+         * @return The builder object.
+         */
+        @NonNull
+        public NetworkStatsCollection.Builder addEntry(@NonNull Key key,
+                @NonNull NetworkStatsHistory history) {
+            Objects.requireNonNull(key);
+            Objects.requireNonNull(history);
+            final List<Entry> historyEntries = history.getEntries();
+
+            final NetworkStatsHistory.Builder historyBuilder =
+                    new NetworkStatsHistory.Builder(mBucketDuration, historyEntries.size());
+            for (Entry entry : historyEntries) {
+                historyBuilder.addEntry(entry);
+            }
+
+            mEntries.put(key, historyBuilder.build());
+            return this;
+        }
+
+        /**
+         * Builds the instance of the {@link NetworkStatsCollection}.
+         *
+         * @return the built instance of {@link NetworkStatsCollection}.
+         */
+        @NonNull
+        public NetworkStatsCollection build() {
+            final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+            for (int i = 0; i < mEntries.size(); i++) {
+                collection.recordHistory(mEntries.keyAt(i), mEntries.valueAt(i));
+            }
+            return collection;
+        }
+    }
+
+    /**
      * the identifier that associate with the {@link NetworkStatsHistory} object to identify
      * a certain record in the {@link NetworkStatsCollection} object.
      */
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index 77b7f16..c2f0cdf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -31,6 +31,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.server.NetworkManagementSocketTagger;
 
@@ -212,6 +213,13 @@
         }
         final NetworkStatsManager statsManager =
                 context.getSystemService(NetworkStatsManager.class);
+        if (statsManager == null) {
+            // TODO: Currently Process.isSupplemental is not working yet, because it depends on
+            //  process to run in a certain UID range, which is not true for now. Change this
+            //  to Log.wtf once Process.isSupplemental is ready.
+            Log.e(TAG, "TrafficStats not initialized, uid=" + Binder.getCallingUid());
+            return;
+        }
         sStatsService = statsManager.getBinder();
     }
 
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index bb123a3..17f3455 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -26,10 +26,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.INetd;
+import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.UnderlyingNetworkInfo;
-import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.SystemClock;
 
@@ -70,7 +70,7 @@
 
     private final boolean mUseBpfStats;
 
-    private final INetd mNetd;
+    private final Context mContext;
 
     /**
      * Guards persistent data access in this class
@@ -158,12 +158,12 @@
         NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
     }
 
-    public NetworkStatsFactory(@NonNull INetd netd) {
-        this(new File("/proc/"), true, netd);
+    public NetworkStatsFactory(@NonNull Context ctx) {
+        this(ctx, new File("/proc/"), true);
     }
 
     @VisibleForTesting
-    public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) {
+    public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats) {
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
         mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
@@ -172,7 +172,7 @@
             mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
             mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
         }
-        mNetd = netd;
+        mContext = ctx;
     }
 
     public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -295,11 +295,12 @@
     }
 
     @GuardedBy("mPersistentDataLock")
-    private void requestSwapActiveStatsMapLocked() throws RemoteException {
-        // Ask netd to do a active map stats swap. When the binder call successfully returns,
+    private void requestSwapActiveStatsMapLocked() {
+        // Do a active map stats swap. When the binder call successfully returns,
         // the system server should be able to safely read and clean the inactive map
         // without race problem.
-        mNetd.trafficSwapActiveStatsMap();
+        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+        cm.swapActiveStatsMap();
     }
 
     /**
@@ -327,7 +328,7 @@
                 if (mUseBpfStats) {
                     try {
                         requestSwapActiveStatsMapLocked();
-                    } catch (RemoteException e) {
+                    } catch (RuntimeException e) {
                         throw new IOException(e);
                     }
                     // Stats are always read from the inactive map, so they must be read after the
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index e85a59e..1953624 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -76,7 +76,7 @@
      */
     public DataUsageRequest register(DataUsageRequest inputRequest, IUsageCallback callback,
             int callingUid, @NetworkStatsAccess.Level int accessLevel) {
-        DataUsageRequest request = buildRequest(inputRequest);
+        DataUsageRequest request = buildRequest(inputRequest, callingUid);
         RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid,
                 accessLevel);
 
@@ -194,10 +194,12 @@
         }
     }
 
-    private DataUsageRequest buildRequest(DataUsageRequest request) {
-        // Cap the minimum threshold to a safe default to avoid too many callbacks
-        long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
-        if (thresholdInBytes < request.thresholdInBytes) {
+    private DataUsageRequest buildRequest(DataUsageRequest request, int callingUid) {
+        // For non-system uid, cap the minimum threshold to a safe default to avoid too
+        // many callbacks.
+        long thresholdInBytes = (callingUid == Process.SYSTEM_UID ? request.thresholdInBytes
+                : Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes));
+        if (thresholdInBytes > request.thresholdInBytes) {
             Log.w(TAG, "Threshold was too low for " + request
                     + ". Overriding to a safer default of " + thresholdInBytes + " bytes");
         }
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index d78c2c4..243d621 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -422,7 +422,7 @@
         final NetworkStatsService service = new NetworkStatsService(context,
                 INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
                 alarmManager, wakeLock, getDefaultClock(),
-                new DefaultNetworkStatsSettings(), new NetworkStatsFactory(netd),
+                new DefaultNetworkStatsSettings(), new NetworkStatsFactory(context),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
 
@@ -1000,8 +1000,17 @@
         }
 
         // TODO: switch to data layer stats once kernel exports
-        // for now, read network layer stats and flatten across all ifaces
-        final NetworkStats networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
+        // for now, read network layer stats and flatten across all ifaces.
+        // This function is used to query NeworkStats for calle's uid. The only caller method
+        // TrafficStats#getDataLayerSnapshotForUid alrady claim no special permission to query
+        // its own NetworkStats.
+        final long ident = Binder.clearCallingIdentity();
+        final NetworkStats networkLayer;
+        try {
+            networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
 
         // splice in operation counts
         networkLayer.spliceOperationsFrom(mUidOperations);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f9ac01d..e4eab4b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1306,8 +1306,8 @@
 
     <!--  Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
     <string name="zen_mode_enable_dialog_turn_on">Turn on</string>
-    <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
-    <string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
+    <!-- Priority mode: Title for the Priority mode dialog to turn on Priority mode. [CHAR LIMIT=50]-->
+    <string name="zen_mode_settings_turn_on_dialog_title" translatable="false">Turn on Priority mode</string>
     <!-- Sound: Summary for the Do not Disturb option when there is no automatic rules turned on. [CHAR LIMIT=NONE]-->
     <string name="zen_mode_settings_summary_off">Never</string>
     <!--[CHAR LIMIT=40] Zen Interruption level: Priority.  -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
index e30aac5..a69c59c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
@@ -16,12 +16,18 @@
 
 package com.android.settingslib.net;
 
-import android.net.NetworkStatsHistory;
+import android.app.usage.NetworkStats;
+
+import java.util.List;
 
 public class ChartData {
-    public NetworkStatsHistory network;
+    // Collect the data usage history of the network from the given {@link NetworkTemplate}.
+    public List<NetworkStats.Bucket> network;
 
-    public NetworkStatsHistory detail;
-    public NetworkStatsHistory detailDefault;
-    public NetworkStatsHistory detailForeground;
+    // Collect the detail datausage history (foreground + Background).
+    public List<NetworkStats.Bucket> detail;
+    // Collect background datausage history.
+    public List<NetworkStats.Bucket> detailDefault;
+    // Collect foreground datausage history.
+    public List<NetworkStats.Bucket> detailForeground;
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
index 60d22a0..573922f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
@@ -19,20 +19,21 @@
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.SET_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
 import android.os.RemoteException;
 
 import com.android.settingslib.AppItem;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Framework loader is deprecated, use the compat version instead.
  *
@@ -42,26 +43,20 @@
 public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
     private static final String KEY_TEMPLATE = "template";
     private static final String KEY_APP = "app";
-    private static final String KEY_FIELDS = "fields";
 
-    private final INetworkStatsSession mSession;
+    private final NetworkStatsManager mNetworkStatsManager;
     private final Bundle mArgs;
 
     public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
-        return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
-    }
-
-    public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
         final Bundle args = new Bundle();
         args.putParcelable(KEY_TEMPLATE, template);
         args.putParcelable(KEY_APP, app);
-        args.putInt(KEY_FIELDS, fields);
         return args;
     }
 
-    public ChartDataLoader(Context context, INetworkStatsSession session, Bundle args) {
+    public ChartDataLoader(Context context, NetworkStatsManager statsManager, Bundle args) {
         super(context);
-        mSession = session;
+        mNetworkStatsManager = statsManager;
         mArgs = args;
     }
 
@@ -75,10 +70,9 @@
     public ChartData loadInBackground() {
         final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
         final AppItem app = mArgs.getParcelable(KEY_APP);
-        final int fields = mArgs.getInt(KEY_FIELDS);
 
         try {
-            return loadInBackground(template, app, fields);
+            return loadInBackground(template, app);
         } catch (RemoteException e) {
             // since we can't do much without history, and we don't want to
             // leave with half-baked UI, we bail hard.
@@ -86,10 +80,22 @@
         }
     }
 
-    private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
+    @NonNull
+    private List<NetworkStats.Bucket> convertToBuckets(@NonNull NetworkStats stats) {
+        final List<NetworkStats.Bucket> ret = new ArrayList<>();
+        while (stats.hasNextBucket()) {
+            final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+            stats.getNextBucket(bucket);
+            ret.add(bucket);
+        }
+        return ret;
+    }
+
+    private ChartData loadInBackground(NetworkTemplate template, AppItem app)
             throws RemoteException {
         final ChartData data = new ChartData();
-        data.network = mSession.getHistoryForNetwork(template, fields);
+        data.network = convertToBuckets(mNetworkStatsManager.queryDetailsForDevice(
+                template, Long.MIN_VALUE, Long.MAX_VALUE));
 
         if (app != null) {
             // load stats for current uid and template
@@ -103,13 +109,13 @@
             }
 
             if (size > 0) {
-                data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
-                data.detail.recordEntireHistory(data.detailDefault);
-                data.detail.recordEntireHistory(data.detailForeground);
+                data.detail = new ArrayList<>();
+                data.detail.addAll(data.detailDefault);
+                data.detail.addAll(data.detailForeground);
             } else {
-                data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+                data.detailDefault = new ArrayList<>();
+                data.detailForeground = new ArrayList<>();
+                data.detail = new ArrayList<>();
             }
         }
 
@@ -129,17 +135,17 @@
     }
 
     /**
-     * Collect {@link NetworkStatsHistory} for the requested UID, combining with
-     * an existing {@link NetworkStatsHistory} if provided.
+     * Collect {@link List<NetworkStats.Bucket>} for the requested UID, combining with
+     * an existing {@link List<NetworkStats.Bucket>} if provided.
      */
-    private NetworkStatsHistory collectHistoryForUid(
-            NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
-            throws RemoteException {
-        final NetworkStatsHistory history = mSession.getHistoryForUid(
-                template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
+    private List<NetworkStats.Bucket> collectHistoryForUid(
+            NetworkTemplate template, int uid, int set, List<NetworkStats.Bucket> existing) {
+        final List<NetworkStats.Bucket> history = convertToBuckets(
+                mNetworkStatsManager.queryDetailsForUidTagState(template,
+                        Long.MIN_VALUE, Long.MAX_VALUE, uid, TAG_NONE, set));
 
         if (existing != null) {
-            existing.recordEntireHistory(history);
+            existing.addAll(history);
             return existing;
         } else {
             return history;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
index 649aeff..8da6032 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
@@ -16,13 +16,12 @@
 
 package com.android.settingslib.net;
 
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
-import android.os.RemoteException;
 
 /**
  * Framework loader is deprecated, use the compat version instead.
@@ -35,7 +34,7 @@
     private static final String KEY_START = "start";
     private static final String KEY_END = "end";
 
-    private final INetworkStatsSession mSession;
+    private final NetworkStatsManager mNetworkStatsManager;
     private final Bundle mArgs;
 
     public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
@@ -46,9 +45,9 @@
         return args;
     }
 
-    public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
+    public SummaryForAllUidLoader(Context context, Bundle args) {
         super(context);
-        mSession = session;
+        mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
         mArgs = args;
     }
 
@@ -63,12 +62,7 @@
         final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
         final long start = mArgs.getLong(KEY_START);
         final long end = mArgs.getLong(KEY_END);
-
-        try {
-            return mSession.getSummaryForAllUid(template, start, end, false);
-        } catch (RemoteException e) {
-            return null;
-        }
+        return mNetworkStatsManager.querySummary(template, start, end);
     }
 
     @Override
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 46e24fa..c6fbfd8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -110,6 +110,7 @@
     <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
     <uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" />
     <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
+    <uses-permission android:name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
     <!-- ACCESS_BACKGROUND_LOCATION is needed for testing purposes only. -->
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <!-- ACCESS_MTP is needed for testing purposes only. -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 3ae85e7..8323e32 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -71,6 +71,7 @@
 yurilin@google.com
 xuqiu@google.com
 zakcohen@google.com
+jernej@google.com
 
 #Android Auto
 hseog@google.com
diff --git a/packages/SystemUI/res/drawable/screenshot_border.xml b/packages/SystemUI/res/drawable/overlay_border.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/screenshot_border.xml
rename to packages/SystemUI/res/drawable/overlay_border.xml
diff --git a/packages/SystemUI/res/drawable/screenshot_cancel.xml b/packages/SystemUI/res/drawable/overlay_cancel.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/screenshot_cancel.xml
rename to packages/SystemUI/res/drawable/overlay_cancel.xml
diff --git a/packages/SystemUI/res/drawable/screenshot_preview_background.xml b/packages/SystemUI/res/drawable/overlay_preview_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/screenshot_preview_background.xml
rename to packages/SystemUI/res/drawable/overlay_preview_background.xml
diff --git a/packages/SystemUI/res/layout/clipboard_content_preview.xml b/packages/SystemUI/res/layout/clipboard_content_preview.xml
deleted file mode 100644
index 7317a94..0000000
--- a/packages/SystemUI/res/layout/clipboard_content_preview.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             xmlns:app="http://schemas.android.com/apk/res-auto"
-             android:id="@+id/preview_border"
-             android:elevation="9dp"
-             android:layout_width="wrap_content"
-             android:layout_height="wrap_content"
-             android:layout_marginStart="@dimen/screenshot_offset_x"
-             android:layout_marginBottom="@dimen/screenshot_offset_y"
-             android:layout_gravity="bottom|start"
-             app:layout_constraintStart_toStartOf="parent"
-             app:layout_constraintBottom_toBottomOf="parent"
-             android:clipToPadding="false"
-             android:clipChildren="false"
-             android:padding="4dp"
-             android:background="@drawable/screenshot_border"
-             >
-    <FrameLayout
-        android:elevation="0dp"
-        android:background="@drawable/screenshot_preview_background"
-        android:clipChildren="true"
-        android:clipToOutline="true"
-        android:clipToPadding="true"
-        android:layout_width="@dimen/screenshot_x_scale"
-        android:layout_height="wrap_content">
-        <TextView android:id="@+id/text_preview"
-                  android:textFontWeight="500"
-                  android:padding="8dp"
-                  android:gravity="center|start"
-                  android:ellipsize="end"
-                  android:autoSizeTextType="uniform"
-                  android:autoSizeMinTextSize="10sp"
-                  android:autoSizeMaxTextSize="200sp"
-                  android:textColor="?android:attr/textColorPrimary"
-                  android:layout_width="@dimen/screenshot_x_scale"
-                  android:layout_height="@dimen/screenshot_x_scale"/>
-        <ImageView
-            android:id="@+id/image_preview"
-            android:scaleType="fitCenter"
-            android:adjustViewBounds="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
-    </FrameLayout>
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 76280d8..7ffb3b2 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -17,9 +17,8 @@
 <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_gravity="bottom"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="match_parent">
     <ImageView
         android:id="@+id/actions_container_background"
         android:visibility="gone"
@@ -57,5 +56,81 @@
                      android:id="@+id/edit_chip"/>
         </LinearLayout>
     </HorizontalScrollView>
-    <include layout="@layout/clipboard_content_preview" />
+    <View
+        android:id="@+id/preview_border"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginStart="@dimen/overlay_offset_x"
+        android:layout_marginBottom="@dimen/overlay_offset_y"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:elevation="@dimen/overlay_preview_elevation"
+        app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
+        app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
+        android:background="@drawable/overlay_border"/>
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/clipboard_preview_end"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierMargin="@dimen/overlay_border_width"
+        app:barrierDirection="end"
+        app:constraint_referenced_ids="clipboard_preview"/>
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/clipboard_preview_top"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="top"
+        app:barrierMargin="@dimen/overlay_border_width_neg"
+        app:constraint_referenced_ids="clipboard_preview"/>
+    <FrameLayout
+        android:id="@+id/clipboard_preview"
+        android:elevation="@dimen/overlay_preview_elevation"
+        android:background="@drawable/overlay_preview_background"
+        android:clipChildren="true"
+        android:clipToOutline="true"
+        android:clipToPadding="true"
+        android:layout_width="@dimen/clipboard_preview_size"
+        android:layout_margin="@dimen/overlay_border_width"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        app:layout_constraintBottom_toBottomOf="@id/preview_border"
+        app:layout_constraintStart_toStartOf="@id/preview_border"
+        app:layout_constraintEnd_toEndOf="@id/preview_border"
+        app:layout_constraintTop_toTopOf="@id/preview_border">
+        <TextView android:id="@+id/text_preview"
+                  android:textFontWeight="500"
+                  android:padding="8dp"
+                  android:gravity="center|start"
+                  android:ellipsize="end"
+                  android:autoSizeTextType="uniform"
+                  android:autoSizeMinTextSize="10sp"
+                  android:autoSizeMaxTextSize="200sp"
+                  android:textColor="?android:attr/textColorPrimary"
+                  android:layout_width="@dimen/clipboard_preview_size"
+                  android:layout_height="@dimen/clipboard_preview_size"/>
+        <ImageView
+            android:id="@+id/image_preview"
+            android:scaleType="fitCenter"
+            android:adjustViewBounds="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </FrameLayout>
+    <FrameLayout
+        android:id="@+id/dismiss_button"
+        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+        android:elevation="@dimen/overlay_dismiss_button_elevation"
+        android:visibility="gone"
+        app:layout_constraintStart_toEndOf="@id/clipboard_preview"
+        app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
+        app:layout_constraintTop_toTopOf="@id/clipboard_preview"
+        app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
+        android:contentDescription="@string/clipboard_dismiss_description">
+        <ImageView
+            android:id="@+id/dismiss_image"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="@dimen/overlay_dismiss_button_margin"
+            android:src="@drawable/overlay_cancel"/>
+    </FrameLayout>
 </com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
index f898ef6..5135947 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -18,7 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/dream_overlay_complications_layer"
-    android:padding="20dp"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <TextClock
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index c6b502e..4929f50 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -28,8 +28,6 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
-    <include layout="@layout/dream_overlay_complications_layer" />
-
     <com.android.systemui.dreams.DreamOverlayStatusBarView
         android:id="@+id/dream_overlay_status_bar"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
index 7c0ee665..227212b 100644
--- a/packages/SystemUI/res/layout/screenshot.xml
+++ b/packages/SystemUI/res/layout/screenshot.xml
@@ -40,7 +40,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"
-        android:elevation="@dimen/screenshot_preview_elevation"
+        android:elevation="@dimen/overlay_preview_elevation"
         android:src="@android:color/white"/>
     <com.android.systemui.screenshot.ScreenshotSelectorView
         android:id="@+id/screenshot_selector"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index f6e3f1a..8f791c3 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -63,20 +63,20 @@
         android:id="@+id/screenshot_preview_border"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_marginStart="@dimen/screenshot_offset_x"
-        android:layout_marginBottom="@dimen/screenshot_offset_y"
-        android:elevation="@dimen/screenshot_preview_elevation"
+        android:layout_marginStart="@dimen/overlay_offset_x"
+        android:layout_marginBottom="@dimen/overlay_offset_y"
+        android:elevation="@dimen/overlay_preview_elevation"
         android:alpha="0"
-        android:background="@drawable/screenshot_border"
+        android:background="@drawable/overlay_border"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="@+id/screenshot_preview_end"
-        app:layout_constraintTop_toTopOf="@+id/screenshot_preview_top"/>
+        app:layout_constraintEnd_toEndOf="@id/screenshot_preview_end"
+        app:layout_constraintTop_toTopOf="@id/screenshot_preview_top"/>
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/screenshot_preview_end"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        app:barrierMargin="4dp"
+        app:barrierMargin="@dimen/overlay_border_width"
         app:barrierDirection="end"
         app:constraint_referenced_ids="screenshot_preview"/>
     <androidx.constraintlayout.widget.Barrier
@@ -84,30 +84,30 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:barrierDirection="top"
-        app:barrierMargin="-4dp"
+        app:barrierMargin="@dimen/overlay_border_width_neg"
         app:constraint_referenced_ids="screenshot_preview"/>
     <ImageView
         android:id="@+id/screenshot_preview"
         android:visibility="invisible"
         android:layout_width="@dimen/screenshot_x_scale"
-        android:layout_margin="4dp"
+        android:layout_margin="@dimen/overlay_border_width"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:elevation="@dimen/screenshot_preview_elevation"
+        android:elevation="@dimen/overlay_preview_elevation"
         android:contentDescription="@string/screenshot_edit_description"
         android:scaleType="fitEnd"
-        android:background="@drawable/screenshot_preview_background"
+        android:background="@drawable/overlay_preview_background"
         android:adjustViewBounds="true"
-        app:layout_constraintBottom_toBottomOf="@+id/screenshot_preview_border"
-        app:layout_constraintStart_toStartOf="@+id/screenshot_preview_border"
-        app:layout_constraintEnd_toEndOf="@+id/screenshot_preview_border"
-        app:layout_constraintTop_toTopOf="@+id/screenshot_preview_border">
+        app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
+        app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
+        app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"
+        app:layout_constraintTop_toTopOf="@id/screenshot_preview_border">
     </ImageView>
     <FrameLayout
         android:id="@+id/screenshot_dismiss_button"
-        android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
-        android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
-        android:elevation="7dp"
+        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+        android:elevation="@dimen/overlay_dismiss_button_elevation"
         android:visibility="gone"
         app:layout_constraintStart_toEndOf="@id/screenshot_preview"
         app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
@@ -118,8 +118,8 @@
             android:id="@+id/screenshot_dismiss_image"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_margin="@dimen/screenshot_dismiss_button_margin"
-            android:src="@drawable/screenshot_cancel"/>
+            android:layout_margin="@dimen/overlay_dismiss_button_margin"
+            android:src="@drawable/overlay_cancel"/>
     </FrameLayout>
     <ImageView
         android:id="@+id/screenshot_scrollable_preview"
@@ -129,5 +129,5 @@
         android:visibility="gone"
         app:layout_constraintStart_toStartOf="@id/screenshot_preview"
         app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-        android:elevation="@dimen/screenshot_preview_elevation"/>
+        android:elevation="@dimen/overlay_preview_elevation"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ceaacfc..67d5b2f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -261,11 +261,6 @@
     <!-- The padding on the global screenshot background image -->
     <dimen name="screenshot_x_scale">80dp</dimen>
     <dimen name="screenshot_bg_protection_height">242dp</dimen>
-    <dimen name="screenshot_preview_elevation">4dp</dimen>
-    <dimen name="screenshot_offset_y">8dp</dimen>
-    <dimen name="screenshot_offset_x">16dp</dimen>
-    <dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
-    <dimen name="screenshot_dismiss_button_margin">8dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">18dp</dimen>
     <dimen name="screenshot_action_container_padding_vertical">4dp</dimen>
     <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
@@ -289,6 +284,19 @@
     <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
     <dimen name="long_screenshot_action_bar_top_margin">8dp</dimen>
 
+    <!-- Dimensions shared between "overlays" (clipboard and screenshot preview UIs) -->
+    <dimen name="overlay_offset_y">8dp</dimen>
+    <dimen name="overlay_offset_x">16dp</dimen>
+    <dimen name="overlay_preview_elevation">4dp</dimen>
+    <dimen name="overlay_dismiss_button_elevation">7dp</dimen>
+    <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen>
+    <dimen name="overlay_dismiss_button_margin">8dp</dimen>
+    <dimen name="overlay_border_width">4dp</dimen>
+    <!-- need a negative margin for some of the constraints. should be overlay_border_width * -1 -->
+    <dimen name="overlay_border_width_neg">-4dp</dimen>
+
+    <dimen name="clipboard_preview_size">@dimen/screenshot_x_scale</dimen>
+
 
     <!-- The width of the view containing navigation buttons -->
     <dimen name="navigation_key_width">70dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 41d5735..7384ccc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -429,8 +429,8 @@
     <string name="accessibility_quick_settings_dnd_none_on">total silence</string>
     <!-- Content description of the do not disturb tile in quick settings when on in alarms only (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_dnd_alarms_on">alarms only</string>
-     <!-- Content description of the do not disturb tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_quick_settings_dnd">Do Not Disturb.</string>
+     <!-- Content description of the priority mode tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_dnd" translatable="false">Priority mode.</string>
     <!-- Content description of the bluetooth tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_bluetooth">Bluetooth.</string>
     <!-- Content description of the bluetooth tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -508,8 +508,8 @@
     <string name="ethernet_label">Ethernet</string>
 
     <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
-    <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_dnd_label">Do Not Disturb</string>
+    <!-- QuickSettings: Priority mode [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_dnd_label" translatable="false">Priority mode</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
     <!-- QuickSettings: Do not disturb - Alarms only [CHAR LIMIT=NONE] -->
     <!-- QuickSettings: Do not disturb - Total silence [CHAR LIMIT=NONE] -->
@@ -896,8 +896,8 @@
     <!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
     <string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
 
-    <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
-    <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
+    <!-- The text to show in the notifications shade when Priority mode is suppressing notifications. [CHAR LIMIT=100] -->
+    <string name="dnd_suppressing_shade_text" translatable="false">Notifications paused by Priority mode</string>
 
     <!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
     <string name="media_projection_action_text">Start now</string>
@@ -1319,8 +1319,8 @@
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
     <string name="notification_channel_summary_priority_baseline">Shows at the top of conversation notifications and as a profile picture on lock screen</string>
     <string name="notification_channel_summary_priority_bubble">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble</string>
-    <string name="notification_channel_summary_priority_dnd">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Do Not Disturb</string>
-    <string name="notification_channel_summary_priority_all">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb</string>
+    <string name="notification_channel_summary_priority_dnd" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Priority mode</string>
+    <string name="notification_channel_summary_priority_all" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Priority mode</string>
 
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
     <string name="notification_priority_title">Priority</string>
@@ -1511,8 +1511,8 @@
     <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
     <string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
 
-    <!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
-    <string name="volume_and_do_not_disturb">Do Not Disturb</string>
+    <!-- SysUI Tuner: Label for screen about priority mode settings [CHAR LIMIT=60] -->
+    <string name="volume_and_do_not_disturb" translatable="false">Priority mode</string>
 
     <!-- SysUI Tuner: Switch to control whether volume buttons enter/exit do
          not disturb [CHAR LIMIT=60] -->
@@ -1873,17 +1873,17 @@
     <!-- Label for when bluetooth is off in QS detail panel [CHAR LIMIT=NONE] -->
     <string name="bt_is_off">Bluetooth is off</string>
 
-    <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] -->
-    <string name="dnd_is_off">Do Not Disturb is off</string>
+    <!-- Label for when Priority mode is off in QS detail panel [CHAR LIMIT=NONE] -->
+    <string name="dnd_is_off" translatable="false">Priority mode is off</string>
 
-    <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] -->
-    <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
+    <!-- Prompt for when Priority mode is on from automatic rule in QS [CHAR LIMIT=NONE] -->
+    <string name="qs_dnd_prompt_auto_rule" translatable="false">Priority mode was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
 
-    <!-- Prompt for when Do not disturb is on from app in QS [CHAR LIMIT=NONE] -->
-    <string name="qs_dnd_prompt_app">Do Not Disturb was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
+    <!-- Prompt for when Priority mode is on from app in QS [CHAR LIMIT=NONE] -->
+    <string name="qs_dnd_prompt_app" translatable="false">Priority mode was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
 
-    <!-- Prompt for when Do not disturb is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
-    <string name="qs_dnd_prompt_auto_rule_app">Do Not Disturb was turned on by an automatic rule or app.</string>
+    <!-- Prompt for when Priority mode is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
+    <string name="qs_dnd_prompt_auto_rule_app" translatable="false">Priority mode was turned on by an automatic rule or app.</string>
 
     <!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
     <string name="running_foreground_services_title">Apps running in background</string>
@@ -2268,8 +2268,8 @@
     <string name="people_tile_description">See recent messages, missed calls, and status updates</string>
     <!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
     <string name="people_tile_title">Conversation</string>
-    <!-- Text when the Conversation widget when Do Not Disturb is suppressing the notification. [CHAR LIMIT=50] -->
-    <string name="paused_by_dnd">Paused by Do Not Disturb</string>
+    <!-- Text when the Conversation widget when Priority mode is suppressing the notification. [CHAR LIMIT=50] -->
+    <string name="paused_by_dnd" translatable="false">Paused by Priority mode</string>
     <!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] -->
     <string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message: <xliff:g id="notification" example="Hey! How is your day going">%2$s</xliff:g></string>
     <!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] -->
@@ -2364,4 +2364,6 @@
     <string name="clipboard_edit_text_copy">Copy</string>
     <!-- Text informing user that content has been copied to the system clipboard [CHAR LIMIT=NONE] -->
     <string name="clipboard_overlay_text_copied">Copied</string>
+    <!-- Label for button to dismiss clipboard overlay [CHAR LIMIT=NONE] -->
+    <string name="clipboard_dismiss_description">Dismiss copy UI</string>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index f2f382d..ace7938 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -277,7 +277,6 @@
                 mLeashMap.put(mOpeningLeashes.get(i), target.leash);
                 t.reparent(target.leash, mInfo.getRootLeash());
                 t.setLayer(target.leash, layer);
-                t.hide(target.leash);
                 targets[i] = target;
             }
             t.apply();
@@ -332,13 +331,6 @@
                 }
             } else {
                 wct = null;
-                if (mOpeningLeashes != null) {
-                    // TODO: the launcher animation should handle this
-                    for (int i = 0; i < mOpeningLeashes.size(); ++i) {
-                        t.show(mOpeningLeashes.get(i));
-                        t.setAlpha(mOpeningLeashes.get(i), 1.f);
-                    }
-                }
                 if (mPipTask != null && mPipTransaction != null) {
                     t.show(mInfo.getChange(mPipTask).getLeash());
                     PictureInPictureSurfaceTransaction.apply(mPipTransaction,
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index ae0702c..caf0307 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -59,6 +59,7 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -91,6 +92,7 @@
     private final WindowManager.LayoutParams mWindowLayoutParams;
     private final PhoneWindow mWindow;
     private final TimeoutHandler mTimeoutHandler;
+    private final AccessibilityManager mAccessibilityManager;
 
     private final DraggableConstraintLayout mView;
     private final ImageView mImagePreview;
@@ -98,6 +100,7 @@
     private final ScreenshotActionChip mEditChip;
     private final ScreenshotActionChip mRemoteCopyChip;
     private final View mActionContainerBackground;
+    private final View mDismissButton;
 
     private Runnable mOnSessionCompleteListener;
 
@@ -113,6 +116,8 @@
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
         mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
 
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+
         mWindowManager = mContext.getSystemService(WindowManager.class);
 
         mTimeoutHandler = timeoutHandler;
@@ -135,10 +140,13 @@
         mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
         mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
         mRemoteCopyChip = requireNonNull(mView.findViewById(R.id.remote_copy_chip));
+        mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
 
         mView.setOnDismissCallback(this::hideImmediate);
         mView.setOnInteractionCallback(() -> mTimeoutHandler.resetTimeout());
 
+        mDismissButton.setOnClickListener(view -> animateOut());
+
         mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
         mRemoteCopyChip.setIcon(
                 Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true);
@@ -158,10 +166,10 @@
         withWindowAttached(() -> {
             mWindow.setContentView(mView);
             updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
-            getEnterAnimation().start();
+            mView.post(() -> getEnterAnimation().start());
         });
 
-        mTimeoutHandler.setOnTimeoutRunnable(() -> animateOut());
+        mTimeoutHandler.setOnTimeoutRunnable(this::animateOut);
 
         mCloseDialogsReceiver = new BroadcastReceiver() {
             @Override
@@ -226,8 +234,8 @@
                         mView.getLocationOnScreen(pt);
                         Rect rect = new Rect(pt[0], pt[1], pt[0] + mView.getWidth(),
                                 pt[1] + mView.getHeight());
-                        if (!rect.contains((int) motionEvent.getRawX(),
-                                (int) motionEvent.getRawY())) {
+                        if (!rect.contains(
+                                (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
                             animateOut();
                         }
                     }
@@ -311,11 +319,15 @@
         ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
 
         mView.setAlpha(0);
+        mDismissButton.setVisibility(View.GONE);
         final View previewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
         final View actionBackground = requireNonNull(
                 mView.findViewById(R.id.actions_container_background));
         mImagePreview.setVisibility(View.VISIBLE);
         mActionContainerBackground.setVisibility(View.VISIBLE);
+        if (mAccessibilityManager.isEnabled()) {
+            mDismissButton.setVisibility(View.VISIBLE);
+        }
 
         anim.addUpdateListener(animation -> {
             mView.setAlpha(animation.getAnimatedFraction());
@@ -385,7 +397,7 @@
 
     private void reset() {
         mView.setTranslationX(0);
-        mView.setAlpha(1);
+        mView.setAlpha(0);
         mTimeoutHandler.cancelTimeout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java
deleted file mode 100644
index 7c3152f..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHost.java
+++ /dev/null
@@ -1,48 +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.systemui.dreams;
-
-import android.view.View;
-
-/**
- * A collection of interfaces related to hosting a complication.
- */
-public abstract class ComplicationHost {
-    /**
-     * An interface for the callback from the complication provider to indicate when the
-     * complication is ready.
-     */
-    public interface CreationCallback {
-        /**
-         * Called to inform the complication view is ready to be placed within the visual space.
-         * @param view The view representing the complication.
-         * @param layoutParams The parameters to create the view with.
-         */
-        void onCreated(View view, ComplicationHostView.LayoutParams layoutParams);
-    }
-
-    /**
-     * An interface for the callback from the complication provider to signal interactions in the
-     * complication.
-     */
-    public interface InteractionCallback {
-        /**
-         * Called to signal the calling complication would like to exit the dream.
-         */
-        void onExit();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java
deleted file mode 100644
index a67dd5c..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationHostView.java
+++ /dev/null
@@ -1,44 +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.systemui.dreams;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-
-/**
- * {@link ComplicationHostView} is the container view for housing complications above of a dream.
- */
-public class ComplicationHostView extends ConstraintLayout {
-    public ComplicationHostView(Context context) {
-        super(context, null);
-    }
-
-    public ComplicationHostView(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    public ComplicationHostView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr, 0);
-    }
-
-    public ComplicationHostView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
deleted file mode 100644
index e5d6319..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.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.systemui.dreams;
-
-import android.annotation.IntDef;
-import android.content.Context;
-
-import com.android.settingslib.dream.DreamBackend;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * {@link ComplicationProvider} is an interface for defining entities that can supply complications
- * to show over a dream. Presentation components such as the {@link DreamOverlayService} supply
- * implementations with the necessary context for constructing such overlays.
- */
-public interface ComplicationProvider {
-    /**
-     * The type of dream complications which can be provided by a {@link ComplicationProvider}.
-     */
-    @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = {
-            COMPLICATION_TYPE_NONE,
-            COMPLICATION_TYPE_TIME,
-            COMPLICATION_TYPE_DATE,
-            COMPLICATION_TYPE_WEATHER,
-            COMPLICATION_TYPE_AIR_QUALITY,
-            COMPLICATION_TYPE_CAST_INFO
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface ComplicationType {}
-
-    int COMPLICATION_TYPE_NONE = 0;
-    int COMPLICATION_TYPE_TIME = 1;
-    int COMPLICATION_TYPE_DATE = 1 << 1;
-    int COMPLICATION_TYPE_WEATHER = 1 << 2;
-    int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
-    int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
-
-    /**
-     * Called when the {@link ComplicationHost} requests the associated complication be produced.
-     *
-     * @param context The {@link Context} used to construct the view.
-     * @param creationCallback The callback to inform the complication has been created.
-     * @param interactionCallback The callback to inform the complication has been interacted with.
-     */
-    void onCreateComplication(Context context, ComplicationHost.CreationCallback creationCallback,
-            ComplicationHost.InteractionCallback interactionCallback);
-
-    /**
-     * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
-     * {@link ComplicationType}.
-     */
-    @ComplicationType
-    default int convertComplicationType(@DreamBackend.ComplicationType int type) {
-        switch (type) {
-            case DreamBackend.COMPLICATION_TYPE_TIME:
-                return COMPLICATION_TYPE_TIME;
-            case DreamBackend.COMPLICATION_TYPE_DATE:
-                return COMPLICATION_TYPE_DATE;
-            case DreamBackend.COMPLICATION_TYPE_WEATHER:
-                return COMPLICATION_TYPE_WEATHER;
-            case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY:
-                return COMPLICATION_TYPE_AIR_QUALITY;
-            case DreamBackend.COMPLICATION_TYPE_CAST_INFO:
-                return COMPLICATION_TYPE_CAST_INFO;
-            default:
-                return COMPLICATION_TYPE_NONE;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 5b46079..be76e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -25,11 +25,11 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
-import androidx.constraintlayout.widget.ConstraintLayout;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
 import com.android.systemui.util.ViewController;
@@ -47,6 +47,8 @@
     private final int mDreamOverlayNotificationsDragAreaHeight;
     private final DreamOverlayStatusBarViewController mStatusBarViewController;
 
+    private final ComplicationHostViewController mComplicationHostViewController;
+
     // The dream overlay's content view, which is located below the status bar (in z-order) and is
     // the space into which widgets are placed.
     private final ViewGroup mDreamOverlayContentView;
@@ -74,6 +76,13 @@
                     final int childCount = mDreamOverlayContentView.getChildCount();
                     for (int i = 0; i < childCount; i++) {
                         View child = mDreamOverlayContentView.getChildAt(i);
+
+                        if (mComplicationHostViewController.getView() == child) {
+                            region.op(mComplicationHostViewController.getTouchRegions(),
+                                    Region.Op.UNION);
+                            continue;
+                        }
+
                         if (child.getGlobalVisibleRect(rect)) {
                             region.op(rect, Region.Op.UNION);
                         }
@@ -93,6 +102,7 @@
     @Inject
     public DreamOverlayContainerViewController(
             DreamOverlayContainerView containerView,
+            ComplicationHostViewComponent.Factory complicationHostViewFactory,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
             DreamOverlayStatusBarViewController statusBarViewController,
             @Main Handler handler,
@@ -106,6 +116,13 @@
                 mView.getResources().getDimensionPixelSize(
                         R.dimen.dream_overlay_notifications_drag_area_height);
 
+        mComplicationHostViewController = complicationHostViewFactory.create().getController();
+        final View view = mComplicationHostViewController.getView();
+
+        mDreamOverlayContentView.addView(view,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+
         mHandler = handler;
         mMaxBurnInOffset = maxBurnInOffset;
         mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
@@ -114,6 +131,7 @@
     @Override
     protected void onInit() {
         mStatusBarViewController.init();
+        mComplicationHostViewController.init();
     }
 
     @Override
@@ -130,18 +148,10 @@
                 .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
     }
 
-    void addOverlay(View overlayView, ConstraintLayout.LayoutParams layoutParams) {
-        mDreamOverlayContentView.addView(overlayView, layoutParams);
-    }
-
     View getContainerView() {
         return mView;
     }
 
-    void removeAllOverlays() {
-        mDreamOverlayContentView.removeAllViews();
-    }
-
     @VisibleForTesting
     int getDreamOverlayNotificationsDragAreaHeight() {
         return mDreamOverlayNotificationsDragAreaHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index a53120f..16ed1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -24,10 +24,13 @@
 import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.ViewModelStore;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.PhoneWindow;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.Complication;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 
 import java.util.concurrent.Executor;
@@ -47,8 +50,6 @@
     private final Context mContext;
     // The Executor ensures actions and ui updates happen on the same thread.
     private final Executor mExecutor;
-    // The state controller informs the service of updates to the complications present.
-    private final DreamOverlayStateController mStateController;
     // A controller for the dream overlay container view (which contains both the status bar and the
     // content area).
     private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
@@ -56,47 +57,51 @@
     // A reference to the {@link Window} used to hold the dream overlay.
     private Window mWindow;
 
-    private final DreamOverlayStateController.Callback mOverlayStateCallback =
-            new DreamOverlayStateController.Callback() {
-                @Override
-                public void onComplicationsChanged() {
-                    mExecutor.execute(() -> reloadComplicationsLocked());
-                }
-            };
+    private final Complication.Host mHost = new Complication.Host() {
+        @Override
+        public void requestExitDream() {
+            mExecutor.execute(DreamOverlayService.this::requestExit);
+        }
+    };
+
+    private final LifecycleRegistry mLifecycleRegistry;
+
+    private ViewModelStore mViewModelStore = new ViewModelStore();
 
     @Inject
     public DreamOverlayService(
             Context context,
             @Main Executor executor,
-            DreamOverlayStateController overlayStateController,
             DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
         mContext = context;
         mExecutor = executor;
-        mStateController = overlayStateController;
-        mDreamOverlayContainerViewController =
-                dreamOverlayComponentFactory.create().getDreamOverlayContainerViewController();
 
-        mStateController.addCallback(mOverlayStateCallback);
+        final DreamOverlayComponent component =
+                dreamOverlayComponentFactory.create(mViewModelStore, mHost);
+        mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController();
+        setCurrentState(Lifecycle.State.CREATED);
+        mLifecycleRegistry = component.getLifecycleRegistry();
+    }
+
+    private void setCurrentState(Lifecycle.State state) {
+        mExecutor.execute(() -> mLifecycleRegistry.setCurrentState(state));
     }
 
     @Override
     public void onDestroy() {
+        setCurrentState(Lifecycle.State.DESTROYED);
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         windowManager.removeView(mWindow.getDecorView());
-        mStateController.removeCallback(mOverlayStateCallback);
         super.onDestroy();
     }
 
     @Override
     public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
-        mExecutor.execute(() -> addOverlayWindowLocked(layoutParams));
-    }
-
-    private void reloadComplicationsLocked() {
-        mDreamOverlayContainerViewController.removeAllOverlays();
-        for (ComplicationProvider overlayProvider : mStateController.getComplications()) {
-            addComplication(overlayProvider);
-        }
+        setCurrentState(Lifecycle.State.STARTED);
+        mExecutor.execute(() -> {
+            addOverlayWindowLocked(layoutParams);
+            setCurrentState(Lifecycle.State.RESUMED);
+        });
     }
 
     /**
@@ -129,20 +134,5 @@
 
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
-        mExecutor.execute(this::reloadComplicationsLocked);
-    }
-
-    @VisibleForTesting
-    protected void addComplication(ComplicationProvider provider) {
-        provider.onCreateComplication(mContext,
-                (view, layoutParams) -> {
-                    // Always move UI related work to the main thread.
-                    mExecutor.execute(() -> mDreamOverlayContainerViewController
-                            .addOverlay(view, layoutParams));
-                },
-                () -> {
-                    // The Callback is set on the main thread.
-                    mExecutor.execute(this::requestExit);
-                });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index 66679bb..e838848 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -17,18 +17,17 @@
 package com.android.systemui.dreams;
 
 import androidx.annotation.NonNull;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.Complication;
 import com.android.systemui.statusbar.policy.CallbackController;
 
-import com.google.common.util.concurrent.ListenableFuture;
-
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -42,35 +41,6 @@
 @SysUISingleton
 public class DreamOverlayStateController implements
         CallbackController<DreamOverlayStateController.Callback> {
-    // A counter for guaranteeing unique complications tokens within the scope of this state
-    // controller.
-    private int mNextComplicationTokenId = 0;
-
-    /**
-     * {@link ComplicationToken} provides a unique key for identifying {@link ComplicationProvider}
-     * instances registered with {@link DreamOverlayStateController}.
-     */
-    public static class ComplicationToken {
-        private final int mId;
-
-        private ComplicationToken(int id) {
-            mId = id;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof ComplicationToken)) return false;
-            ComplicationToken that = (ComplicationToken) o;
-            return mId == that.mId;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mId);
-        }
-    }
-
     /**
      * Callback for dream overlay events.
      */
@@ -84,7 +54,8 @@
 
     private final Executor mExecutor;
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
-    private final HashMap<ComplicationToken, ComplicationProvider> mComplications = new HashMap<>();
+
+    private final Collection<Complication> mComplications = new HashSet();
 
     @VisibleForTesting
     @Inject
@@ -93,47 +64,32 @@
     }
 
     /**
-     * Adds a complication to be presented on top of dreams.
-     * @param provider The {@link ComplicationProvider} providing the dream.
-     * @return The {@link ComplicationToken} tied to the supplied {@link ComplicationProvider}.
+     * Adds a complication to be included on the dream overlay.
      */
-    public ListenableFuture<ComplicationToken> addComplication(ComplicationProvider provider) {
-        return CallbackToFutureAdapter.getFuture(completer -> {
-            mExecutor.execute(() -> {
-                final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++);
-                mComplications.put(token, provider);
-                notifyCallbacks();
-                completer.set(token);
-            });
-            return "DreamOverlayStateController::addComplication";
+    public void addComplication(Complication complication) {
+        mExecutor.execute(() -> {
+            if (mComplications.add(complication)) {
+                mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+            }
         });
     }
 
     /**
-     * Removes a complication from being shown on dreams.
-     * @param token The {@link ComplicationToken} associated with the {@link ComplicationProvider}
-     *              to be removed.
-     * @return The removed {@link ComplicationProvider}, {@code null} if not found.
+     * Removes a complication from inclusion on the dream overlay.
      */
-    public ListenableFuture<ComplicationProvider> removeComplication(ComplicationToken token) {
-        return CallbackToFutureAdapter.getFuture(completer -> {
-            mExecutor.execute(() -> {
-                final ComplicationProvider removedComplication = mComplications.remove(token);
-
-                if (removedComplication != null) {
-                    notifyCallbacks();
-                }
-                completer.set(removedComplication);
-            });
-
-            return "DreamOverlayStateController::removeComplication";
+    public void removeComplication(Complication complication) {
+        mExecutor.execute(() -> {
+            if (mComplications.remove(complication)) {
+                mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+            }
         });
     }
 
-    private void notifyCallbacks() {
-        for (Callback callback : mCallbacks) {
-            callback.onComplicationsChanged();
-        }
+    /**
+     * Returns collection of present {@link Complication}.
+     */
+    public Collection<Complication> getComplications() {
+        return Collections.unmodifiableCollection(mComplications);
     }
 
     @Override
@@ -161,12 +117,4 @@
             mCallbacks.remove(callback);
         });
     }
-
-    /**
-     * Returns all registered {@link ComplicationProvider} instances.
-     * @return A collection of {@link ComplicationProvider}.
-     */
-    public Collection<ComplicationProvider> getComplications() {
-        return mComplications.values();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
new file mode 100644
index 0000000..96cf50d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import android.annotation.IntDef;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * {@link Complication} is an interface for defining a complication, a visual component rendered
+ * above a dream. {@link Complication} instances encapsulate the logic for generating the view to be
+ * shown, along with the supporting control/logic. The decision for including the
+ * {@link Complication} is not the responsibility of the {@link Complication}. This is instead
+ * handled by domain logic and invocations to add and remove the {@link Complication} through
+ * {@link com.android.systemui.dreams.DreamOverlayStateController#addComplication(Complication)} and
+ * {@link com.android.systemui.dreams.DreamOverlayStateController#removeComplication(Complication)}.
+ * A {@link Complication} also does not represent a specific instance of the view. Instead, it
+ * should be viewed as a provider, where view instances are requested from it. The associated
+ * {@link ViewHolder} interface is requested for each view request. This object is retained for the
+ * view's lifetime, providing a container for any associated logic. The complication rendering
+ * system will consult this {@link ViewHolder} for {@link View} to show and
+ * {@link ComplicationLayoutParams} to position the view. {@link ComplicationLayoutParams} allow for
+ * specifying the sizing and position of the {@link Complication}.
+ *
+ * The following code sample exhibits the entities and lifecycle involved with a
+ * {@link Complication}.
+ *
+ * <pre>{@code
+ * // This component allows for the complication to generate a new ViewHolder for every request.
+ * @Subcomponent
+ * interface ExampleViewHolderComponent {
+ *     @Subcomponent.Factory
+ *     interface Factory {
+ *         ExampleViewHolderComponent create();
+ *     }
+ *
+ *     ExampleViewHolder getViewHolder();
+ * }
+ *
+ * // An example entity that controls whether or not a complication should be included on dreams.
+ * // Note how the complication is tracked by reference for removal.
+ * public class ExampleComplicationProvider {
+ *     private final DreamOverlayStateController mDreamOverlayStateController;
+ *     private final ExampleComplication mComplication;
+ *     @Inject
+ *     public ExampleComplicationProvider(
+ *             ExampleComplication complication,
+ *             DreamOverlayStateController stateController) {
+ *         mDreamOverlayStateController = stateController;
+ *         mComplication = complication;
+ *     }
+ *
+ *     public void onShowConditionsMet(boolean met) {
+ *         if (met) {
+ *             mDreamOverlayStateController.addComplication(mComplication);
+ *         } else {
+ *             mDreamOverlayStateController.removeComplication(mComplication);
+ *         }
+ *     }
+ * }
+ *
+ * // An example complication. Note how a factory is created to supply a unique ViewHolder for each
+ * // request. Also, there is no particular view instance members defined in the complication.
+ * class ExampleComplication implements Complication {
+ *     private final ExampleViewHolderComponent.Factory mFactory;
+ *     @Inject
+ *     public ExampleComplication(ExampleViewHolderComponent.Factory viewHolderComponentFactory) {
+ *         mFactory = viewHolderComponentFactory;
+ *     }
+ *
+ *     @Override
+ *     public ViewHolder createView(ComplicationViewModel model) {
+ *         return mFactory.create().getViewHolder();
+ *     }
+ * }
+ *
+ * // Not every ViewHolder needs to include a view controller. It is included here as an example of
+ * // how such logic can be contained and associated with the ViewHolder lifecycle.
+ * class ExampleViewController extends ViewController<FrameLayout> {
+ *     protected ExampleViewController(FrameLayout view) {
+ *         super(view);
+ *     }
+ *
+ *     @Override
+ *     protected void onViewAttached() { }
+ *
+ *     @Override
+ *     protected void onViewDetached() { }
+ * }
+ *
+ * // An example ViewHolder. This is the correct place to contain any value/logic associated with a
+ * // particular instance of the ComplicationView.
+ * class ExampleViewHolder implements Complication.ViewHolder {
+ *     final FrameLayout mView;
+ *     final ExampleViewController mController;
+ *
+ *     @Inject
+ *     public ExampleViewHolder(Context context) {
+ *         mView = new FrameLayout(context);
+ *         mController = new ExampleViewController(mView);
+ *     }
+ *     @Override
+ *     public View getView() {
+ *         return mView;
+ *     }
+ *
+ *     @Override
+ *     public ComplicationLayoutParams getLayoutParams() {
+ *         return new ComplicationLayoutParams(
+ *                 200,
+ *                 100,
+ *                 ComplicationLayoutParams.POSITION_TOP | ComplicationLayoutParams.DIRECTION_END,
+ *                 ComplicationLayoutParams.DIRECTION_DOWN,
+ *                 4);
+ *     }
+ * }
+ * }
+ * </pre>
+ */
+public interface Complication {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CATEGORY_" }, value = {
+            CATEGORY_STANDARD,
+            CATEGORY_SYSTEM,
+    })
+
+    @interface Category {}
+    /**
+     * {@code CATEGORY_STANDARD} indicates the complication is a normal component. Rules and
+     * settings, such as hiding all complications, will apply to this complication.
+     */
+    int CATEGORY_STANDARD = 1 << 0;
+    /**
+     * {@code CATEGORY_SYSTEM} indicates complications driven by SystemUI. Usually, these are
+     * core components that are not user controlled. These can potentially deviate from given
+     * rule sets that would normally apply to {@code CATEGORY_STANDARD}.
+     */
+    int CATEGORY_SYSTEM = 1 << 1;
+
+    /**
+     * The {@link Host} interface specifies a way a {@link Complication} to communicate with its
+     * parent entity for information and actions.
+     */
+    interface Host {
+        /**
+         * Called to signal a {@link Complication} has requested to exit the dream.
+         */
+        void requestExitDream();
+    }
+
+    /**
+     * Returned through {@link Complication#createView(ComplicationViewModel)}, {@link ViewHolder}
+     * is a container for a single {@link Complication} instance. The {@link Host} guarantees that
+     * the {@link ViewHolder} will be retained for the lifetime of the {@link Complication}
+     * instance's user. The view is responsible for providing the view that represents the
+     * {@link Complication}. This object is the proper place to store any related entities, such as
+     * a {@link com.android.systemui.util.ViewController} for the view.
+     */
+    interface ViewHolder {
+        /**
+         * Returns the {@link View} associated with the {@link ViewHolder}. This {@link View} should
+         * be stable and generated once.
+         * @return
+         */
+        View getView();
+
+        /**
+         * Returns the {@link Category} associated with the {@link Complication}. {@link Category}
+         * is a grouping which helps define the relationship of the {@link Complication} to
+         * System UI and the rest of the system. It is used for presentation and other decisions.
+         */
+        @Complication.Category
+        default int getCategory() {
+            return Complication.CATEGORY_STANDARD;
+        }
+
+        /**
+         * Returns the {@link ComplicationLayoutParams} associated with this complication. The
+         * values expressed here are treated as preference rather than requirement. The hosting
+         * entity is free to modify/interpret the parameters as deemed fit.
+         */
+        ComplicationLayoutParams getLayoutParams();
+    }
+
+    /**
+     * Generates a {@link ViewHolder} for the {@link Complication}. This captures both the view and
+     * control logic for a single instance of the complication. The {@link Complication} may be
+     * asked at any time to generate another view.
+     * @param model The {@link ComplicationViewModel} associated with this particular
+     *              {@link Complication} instance.
+     * @return a {@link ViewHolder} for this {@link Complication} instance.
+     */
+    ViewHolder createView(ComplicationViewModel model);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java
new file mode 100644
index 0000000..76818fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveData.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import androidx.lifecycle.LiveData;
+
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+/**
+ * {@link ComplicationCollectionLiveData} wraps
+ * {@link DreamOverlayStateController#getComplications()} to provide an observable
+ * {@link Complication} data set tied to a lifecycle. This should not be directly accessed. Instead,
+ * clients should access the data from {@link ComplicationCollectionViewModel}.
+ */
+public class ComplicationCollectionLiveData extends LiveData<Collection<Complication>> {
+    final DreamOverlayStateController mDreamOverlayStateController;
+
+    final DreamOverlayStateController.Callback mStateControllerCallback;
+
+    {
+        mStateControllerCallback = new DreamOverlayStateController.Callback() {
+            @Override
+            public void onComplicationsChanged() {
+                setValue(mDreamOverlayStateController.getComplications());
+            }
+
+        };
+    }
+
+    @Inject
+    public ComplicationCollectionLiveData(DreamOverlayStateController stateController) {
+        super();
+        mDreamOverlayStateController = stateController;
+    }
+
+    @Override
+    protected void onActive() {
+        super.onActive();
+        mDreamOverlayStateController.addCallback(mStateControllerCallback);
+        setValue(mDreamOverlayStateController.getComplications());
+    }
+
+    @Override
+    protected void onInactive() {
+        mDreamOverlayStateController.removeCallback(mStateControllerCallback);
+        super.onInactive();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionViewModel.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionViewModel.java
new file mode 100644
index 0000000..7190d7a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationCollectionViewModel.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Transformations;
+import androidx.lifecycle.ViewModel;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+/**
+ * {@link ComplicationCollectionViewModel} is an abstraction for observing and accessing
+ * {@link ComplicationViewModel} for registered {@link Complication}.
+ */
+public class ComplicationCollectionViewModel extends ViewModel {
+    private final LiveData<Collection<ComplicationViewModel>> mComplications;
+    private final ComplicationViewModelTransformer mTransformer;
+
+    /**
+     * Injectable constructor for {@link ComplicationCollectionViewModel}. Note that this cannot
+     * be implicitly injected. Clients must bind scoped instance values through the corresponding
+     * dagger subcomponent.
+     */
+    @Inject
+    public ComplicationCollectionViewModel(
+            ComplicationCollectionLiveData complications,
+            ComplicationViewModelTransformer transformer) {
+        mComplications = Transformations.map(complications, collection -> convert(collection));
+        mTransformer = transformer;
+    }
+
+    private Collection<ComplicationViewModel> convert(Collection<Complication> complications) {
+        return complications
+                .stream()
+                .map(complication -> mTransformer.getViewModel(complication))
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns {@link LiveData} for the collection of {@link Complication} represented as
+     * {@link ComplicationViewModel}.
+     */
+    public LiveData<Collection<ComplicationViewModel>> getComplications() {
+        return mComplications;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
new file mode 100644
index 0000000..f627f15
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent.SCOPED_COMPLICATIONS_LAYOUT;
+import static com.android.systemui.dreams.complication.dagger.ComplicationModule.SCOPED_COMPLICATIONS_MODEL;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.util.ViewController;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * The {@link ComplicationHostViewController} is responsible for displaying complications within
+ * a given container. It monitors the available {@link Complication} instances from
+ * {@link com.android.systemui.dreams.DreamOverlayStateController} and inserts/removes them through
+ * a {@link ComplicationLayoutEngine}.
+ */
+public class ComplicationHostViewController extends ViewController<ConstraintLayout> {
+    private final ComplicationLayoutEngine mLayoutEngine;
+    private final LifecycleOwner mLifecycleOwner;
+    private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
+    private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+
+    @Inject
+    protected ComplicationHostViewController(
+            @Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout view,
+            ComplicationLayoutEngine layoutEngine,
+            LifecycleOwner lifecycleOwner,
+            @Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel) {
+        super(view);
+        mLayoutEngine = layoutEngine;
+        mLifecycleOwner = lifecycleOwner;
+        mComplicationCollectionViewModel = viewModel;
+    }
+
+    @Override
+    protected void onInit() {
+        super.onInit();
+        mComplicationCollectionViewModel.getComplications().observe(mLifecycleOwner,
+                complicationViewModels -> updateComplications(complicationViewModels));
+    }
+
+    /**
+     * Returns the region in display space occupied by complications. Touches in this region
+     * (composed of a collection of individual rectangular regions) should be directed to the
+     * complications rather than the region underneath.
+     */
+    public Region getTouchRegions() {
+        final Region region = new Region();
+        final Rect rect = new Rect();
+        final int childCount = mView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mView.getChildAt(i);
+            if (child.getGlobalVisibleRect(rect)) {
+                region.op(rect, Region.Op.UNION);
+            }
+        }
+
+        return region;
+    }
+
+    private void updateComplications(Collection<ComplicationViewModel> complications) {
+        final Collection<ComplicationId> ids = complications.stream()
+                .map(complicationViewModel -> complicationViewModel.getId())
+                .collect(Collectors.toSet());
+
+        final Collection<ComplicationId> removedComplicationIds =
+                mComplications.keySet().stream()
+                        .filter(complicationId -> !ids.contains(complicationId))
+                        .collect(Collectors.toSet());
+
+        // Trim removed complications
+        removedComplicationIds.forEach(complicationId -> {
+            mLayoutEngine.removeComplication(complicationId);
+            mComplications.remove(complicationId);
+        });
+
+        // Add new complications
+        final Collection<ComplicationViewModel> newComplications = complications
+                .stream()
+                .filter(complication -> !mComplications.containsKey(complication.getId()))
+                .collect(Collectors.toSet());
+
+        newComplications
+                .forEach(complication -> {
+                    final ComplicationId id = complication.getId();
+                    final Complication.ViewHolder viewHolder = complication.getComplication()
+                            .createView(complication);
+                    mComplications.put(id, viewHolder);
+                    mLayoutEngine.addComplication(id, viewHolder.getView(),
+                            viewHolder.getLayoutParams(), viewHolder.getCategory());
+                });
+    }
+
+    @Override
+    protected void onViewAttached() {
+    }
+
+    @Override
+    protected void onViewDetached() {
+    }
+
+    /**
+     * Exposes the associated {@link View}. Since this {@link View} is instantiated through dagger
+     * in the {@link ComplicationHostViewController} constructor, the
+     * {@link ComplicationHostViewController} is responsible for surfacing it so that it can be
+     * included in the parent view hierarchy.
+     */
+    public View getView() {
+        return mView;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationId.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationId.java
new file mode 100644
index 0000000..420359c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationId.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+/**
+ * A {@link ComplicationId} is a value to uniquely identify a complication during the current
+ * runtime and within a particular scope. Any guarantees beyond this will need to be enforced
+ * externally.
+ */
+public class ComplicationId {
+    /**
+     * An associated factory for minting ids that are unique in for the factory's scope.
+     */
+    public static class Factory {
+        private int mNextId;
+
+        ComplicationId getNextId() {
+            return new ComplicationId(mNextId++);
+        }
+    }
+
+    private int mId;
+
+    private ComplicationId(int id) {
+        mId = id;
+    }
+
+    @Override
+    public String toString() {
+        return "ComplicationId{" + "mId=" + mId + "}";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
new file mode 100644
index 0000000..cb24ae6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent.SCOPED_COMPLICATIONS_LAYOUT;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.Constraints;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ComplicationLayoutEngine} arranges a collection of {@link ComplicationViewModel} based on
+ * their layout parameters and attributes. The management of this set is done by
+ * {@link ComplicationHostViewController}.
+ */
+public class ComplicationLayoutEngine  {
+    public static final String TAG = "ComplicationLayoutEngine";
+
+    /**
+     * {@link ViewEntry} is an internal container, capturing information necessary for working with
+     * a particular {@link Complication} view.
+     */
+    private static class ViewEntry implements Comparable<ViewEntry> {
+        private final View mView;
+        private final ComplicationLayoutParams mLayoutParams;
+        private final Parent mParent;
+        @Complication.Category
+        private final int mCategory;
+
+        /**
+         * Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
+         * view hierarchy to be accessed without traversing the entire view tree.
+         */
+        ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent) {
+            mView = view;
+            // Views that are generated programmatically do not have a unique id assigned to them
+            // at construction. A new id is assigned here to enable ConstraintLayout relative
+            // specifications. Existing ids for inflated views are not preserved.
+            // {@link Complication.ViewHolder} should not reference the root container by id.
+            mView.setId(View.generateViewId());
+            mLayoutParams = layoutParams;
+            mCategory = category;
+            mParent = parent;
+        }
+
+        /**
+         * Returns the {@link View} associated with the {@link Complication}. This is the instance
+         * passed in at construction. The reference to this {@link View} is captured when the
+         * {@link Complication} is added to the {@link ComplicationLayoutEngine}. The
+         * {@link Complication} cannot modify the {@link View} reference beyond this point.
+         */
+        private View getView() {
+            return mView;
+        }
+
+        /**
+         * Returns The {@link ComplicationLayoutParams} associated with the view.
+         */
+        public ComplicationLayoutParams getLayoutParams() {
+            return mLayoutParams;
+        }
+
+        /**
+         * Interprets the {@link #getLayoutParams()} into {@link ConstraintLayout.LayoutParams} and
+         * applies them to the view. The method accounts for the relationship of the {@link View} to
+         * the other {@link Complication} views around it. The organization of the {@link View}
+         * instances in {@link ComplicationLayoutEngine} can be seen as lists. A {@link View} is
+         * either the head of its list or a following node. This head is passed into this method,
+         * which can be a reference to the {@link View} to indicate it is the head.
+         */
+        public void applyLayoutParams(View head) {
+            // Only the basic dimension parameters from the base ViewGroup.LayoutParams are carried
+            // over verbatim from the complication specified LayoutParam. Other fields are
+            // interpreted.
+            final ConstraintLayout.LayoutParams params =
+                    new Constraints.LayoutParams(mLayoutParams.width, mLayoutParams.height);
+
+            final int direction = getLayoutParams().getDirection();
+
+            // If no parent, view is the anchor. In this case, it is given the highest priority for
+            // alignment. All alignment preferences are done in relation to the parent container.
+            final boolean isRoot = head == mView;
+
+            // Each view can be seen as a vector, having a point (described here as position) and
+            // direction. When a view is the head of a position, then it is the first in a sequence
+            // of complications to appear from that position. For example, being the head for
+            // position POSITION_TOP | POSITION_END will cause the view to be shown as the first
+            // view in that corner. In this case, the positions specify which sides to align with
+            // the parent. If the view is not the head, the positions perpendicular to the direction
+            // of the view specify which side to align with the opposing side of the head view.
+            // Otherwise, the position aligns with the containing view. This means a
+            // POSITION_BOTTOM | POSITION_START with DIRECTION_UP non-head view's bottom to be
+            // aligned with the preceding view node's top and start to be aligned with the
+            // parent's start.
+            mLayoutParams.iteratePositions(position -> {
+                switch(position) {
+                    case ComplicationLayoutParams.POSITION_START:
+                        if (isRoot || direction != ComplicationLayoutParams.DIRECTION_END) {
+                            params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
+                        } else {
+                            params.startToEnd = head.getId();
+                        }
+                        break;
+                    case ComplicationLayoutParams.POSITION_TOP:
+                        if (isRoot || direction != ComplicationLayoutParams.DIRECTION_DOWN) {
+                            params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
+                        } else {
+                            params.topToBottom = head.getId();
+                        }
+                        break;
+                    case ComplicationLayoutParams.POSITION_BOTTOM:
+                        if (isRoot || direction != ComplicationLayoutParams.DIRECTION_UP) {
+                            params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
+                        } else {
+                            params.bottomToTop = head.getId();
+                        }
+                        break;
+                    case ComplicationLayoutParams.POSITION_END:
+                        if (isRoot || direction != ComplicationLayoutParams.DIRECTION_START) {
+                            params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
+                        } else {
+                            params.endToStart = head.getId();
+                        }
+                        break;
+                }
+            });
+
+            mView.setLayoutParams(params);
+        }
+
+        /**
+         * Informs the {@link ViewEntry}'s parent entity to remove the {@link ViewEntry} from
+         * being shown further.
+         */
+        public void remove() {
+            mParent.removeEntry(this);
+
+            ((ViewGroup) mView.getParent()).removeView(mView);
+        }
+
+        @Override
+        public int compareTo(ViewEntry viewEntry) {
+            // If the two entries have different categories, system complications take precedence.
+            if (viewEntry.mCategory != mCategory) {
+                // Note that this logic will need to be adjusted if more categories are introduced.
+                return mCategory == Complication.CATEGORY_SYSTEM ? 1 : -1;
+            }
+
+            // A higher weight indicates greater precedence if all else being equal.
+            if (viewEntry.mLayoutParams.getWeight() != mLayoutParams.getWeight()) {
+                return mLayoutParams.getWeight() > viewEntry.mLayoutParams.getWeight() ? 1 : -1;
+            }
+
+            return 0;
+        }
+
+        /**
+         * {@link Builder} allows for a multiple entities to contribute to the {@link ViewEntry}
+         * construction. This is necessary for setting an immutable parent, which might not be
+         * known until the view hierarchy is traversed.
+         */
+        private static class Builder {
+            private final View mView;
+            private final ComplicationLayoutParams mLayoutParams;
+            private final int mCategory;
+            private Parent mParent;
+
+            Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) {
+                mView = view;
+                mLayoutParams = lp;
+                mCategory = category;
+            }
+
+            /**
+             * Returns the set {@link ComplicationLayoutParams}
+             */
+            public ComplicationLayoutParams getLayoutParams() {
+                return mLayoutParams;
+            }
+
+            /**
+             * Returns the set {@link Complication.Category}.
+             */
+            @Complication.Category
+            public int getCategory() {
+                return mCategory;
+            }
+
+            /**
+             * Sets the parent. Note that this references to the entity for handling events, such as
+             * requesting the removal of the {@link View}. It is not the
+             * {@link android.view.ViewGroup} which contains the {@link View}.
+             */
+            Builder setParent(Parent parent) {
+                mParent = parent;
+                return this;
+            }
+
+            /**
+             * Builds and returns the resulting {@link ViewEntry}.
+             */
+            ViewEntry build() {
+                return new ViewEntry(mView, mLayoutParams, mCategory, mParent);
+            }
+        }
+
+        /**
+         * An interface allowing an {@link ViewEntry} to signal events.
+         */
+        interface Parent {
+            /**
+             * Indicates the {@link ViewEntry} requests removal.
+             */
+            void removeEntry(ViewEntry entry);
+        }
+    }
+
+    /**
+     * {@link PositionGroup} represents a collection of {@link Complication} at a given location.
+     * It further organizes the {@link Complication} by the direction in which they emanate from
+     * this position.
+     */
+    private static class PositionGroup implements DirectionGroup.Parent {
+        private final HashMap<Integer, DirectionGroup> mDirectionGroups = new HashMap<>();
+
+        /**
+         * Invoked by the {@link PositionGroup} holder to introduce a {@link Complication} view to
+         * this group. It is assumed that the caller has correctly identified this
+         * {@link PositionGroup} as the proper home for the {@link Complication} based on its
+         * declared position.
+         */
+        public ViewEntry add(ViewEntry.Builder entryBuilder) {
+            final int direction = entryBuilder.getLayoutParams().getDirection();
+            if (!mDirectionGroups.containsKey(direction)) {
+                mDirectionGroups.put(direction, new DirectionGroup(this));
+            }
+
+            return mDirectionGroups.get(direction).add(entryBuilder);
+        }
+
+        @Override
+        public void onEntriesChanged() {
+            // Whenever an entry is added/removed from a child {@link DirectionGroup}, it is vital
+            // that all {@link DirectionGroup} children are visited. It is possible the overall
+            // head has changed, requiring constraints to be adjusted.
+            updateViews();
+        }
+
+        private void updateViews() {
+            ViewEntry head = null;
+
+            // Identify which {@link Complication} head from the set of {@link DirectionGroup}
+            // should be treated as the {@link PositionGroup} head.
+            for (DirectionGroup directionGroup : mDirectionGroups.values()) {
+                final ViewEntry groupHead = directionGroup.getHead();
+                if (head == null || (groupHead != null && groupHead.compareTo(head) > 0)) {
+                    head = groupHead;
+                }
+            }
+
+            // A headless position group indicates no complications.
+            if (head == null) {
+                return;
+            }
+
+            for (DirectionGroup directionGroup : mDirectionGroups.values()) {
+                // Tell each {@link DirectionGroup} to update its containing {@link ViewEntry} based
+                // on the identified head. This iteration will also capture any newly added views.
+                directionGroup.updateViews(head.getView());
+            }
+        }
+    }
+
+    /**
+     * A {@link DirectionGroup} organizes the {@link ViewEntry} of a parent group that point are
+     * laid out in the same direction.
+     */
+    private static class DirectionGroup implements ViewEntry.Parent {
+        /**
+         * An interface implemented by the {@link DirectionGroup} parent to receive updates.
+         */
+        interface Parent {
+            /**
+             * Invoked to indicate a change to the {@link ViewEntry} composition for this
+             * {@link DirectionGroup}.
+             */
+            void onEntriesChanged();
+        }
+        private final ArrayList<ViewEntry> mViews = new ArrayList<>();
+        private final Parent mParent;
+
+        /**
+         * Creates a new {@link DirectionGroup} with the specified parent. Note that the
+         * {@link DirectionGroup} does not store its own direction. It is the responsibility of the
+         * {@link DirectionGroup.Parent} to maintain this association.
+         */
+        DirectionGroup(Parent parent) {
+            mParent = parent;
+        }
+
+        /**
+         * Returns the head of the group. It is assumed that the order of the {@link ViewEntry} is
+         * proactively maintained.
+         */
+        public ViewEntry getHead() {
+            return mViews.isEmpty() ? null : mViews.get(0);
+        }
+
+        /**
+         * Adds a {@link ViewEntry} via {@link ViewEntry.Builder} to this group.
+         */
+        public ViewEntry add(ViewEntry.Builder entryBuilder) {
+            final ViewEntry entry = entryBuilder.setParent(this).build();
+            mViews.add(entry);
+
+            // After adding view, reverse sort collection.
+            Collections.sort(mViews);
+            Collections.reverse(mViews);
+
+            mParent.onEntriesChanged();
+
+            return entry;
+        }
+
+        @Override
+        public void removeEntry(ViewEntry entry) {
+            // Sort is handled when the view is added, so should still be correct after removal.
+            // However, the head may have been removed, which may affect the layout of views in
+            // other DirectionGroups of the same PositionGroup.
+            mViews.remove(entry);
+            mParent.onEntriesChanged();
+        }
+
+        /**
+         * Invoked by {@link Parent} to update the layout of all children {@link ViewEntry} with
+         * the specified head. Note that the head might not be in this group and instead part of a
+         * neighboring group.
+         */
+        public void updateViews(View groupHead) {
+            Iterator<ViewEntry> it = mViews.iterator();
+
+            while (it.hasNext()) {
+                final ViewEntry viewEntry = it.next();
+                viewEntry.applyLayoutParams(groupHead);
+                groupHead = viewEntry.getView();
+            }
+        }
+    }
+
+    private final ConstraintLayout mLayout;
+    private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
+    private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
+
+    /** */
+    @Inject
+    public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout) {
+        mLayout = layout;
+    }
+
+    /**
+     * Adds a complication to this {@link ComplicationLayoutEngine}.
+     * @param id A {@link ComplicationId} unique to this complication. If this matches a
+     *           complication within this {@link ComplicationViewModel}, the existing complication
+     *           will be removed.
+     * @param view The {@link View} to be shown.
+     * @param lp The {@link ComplicationLayoutParams} as expressed by the {@link Complication}.
+     *           These will be interpreted into the final applied parameters.
+     * @param category The {@link Complication.Category} for the {@link Complication}.
+     */
+    public void addComplication(ComplicationId id, View view,
+            ComplicationLayoutParams lp, @Complication.Category int category) {
+        // If the complication is present, remove.
+        if (mEntries.containsKey(id)) {
+            removeComplication(id);
+        }
+
+        final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category);
+
+        // Add position group if doesn't already exist
+        final int position = lp.getPosition();
+        if (!mPositions.containsKey(position)) {
+            mPositions.put(position, new PositionGroup());
+        }
+
+        // Insert entry into group
+        final ViewEntry entry = mPositions.get(position).add(entryBuilder);
+        mEntries.put(id, entry);
+
+        mLayout.addView(entry.getView());
+    }
+
+    /**
+     * Removes a complication by {@link ComplicationId}.
+     */
+    public void removeComplication(ComplicationId id) {
+        if (!mEntries.containsKey(id)) {
+            Log.e(TAG, "could not find id:" + id);
+            return;
+        }
+
+        final ViewEntry entry = mEntries.get(id);
+        entry.remove();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
new file mode 100644
index 0000000..f9a69fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import android.annotation.IntDef;
+import android.view.ViewGroup;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * {@link ComplicationLayoutParams} allows a {@link Complication} to express its preferred location
+ * and dimensions. Note that these parameters are not directly applied by any {@link ViewGroup}.
+ * They are instead consulted for the final parameters which best seem fit for usage.
+ */
+public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "POSITION_" }, value = {
+            POSITION_TOP,
+            POSITION_END,
+            POSITION_BOTTOM,
+            POSITION_START,
+    })
+
+    @interface Position {}
+    /** Align view with the top of parent or bottom of preceding {@link Complication}. */
+    static final int POSITION_TOP = 1 << 0;
+    /** Align view with the bottom of parent or top of preceding {@link Complication}. */
+    static final int POSITION_BOTTOM = 1 << 1;
+    /** Align view with the start of parent or end of preceding {@link Complication}. */
+    static final int POSITION_START = 1 << 2;
+    /** Align view with the end of parent or start of preceding {@link Complication}. */
+    static final int POSITION_END = 1 << 3;
+
+    private static final int FIRST_POSITION = POSITION_TOP;
+    private static final int LAST_POSITION = POSITION_END;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "DIRECTION_" }, value = {
+            DIRECTION_UP,
+            DIRECTION_DOWN,
+            DIRECTION_START,
+            DIRECTION_END,
+    })
+
+    @interface Direction {}
+    /** Position view upward from position. */
+    static final int DIRECTION_UP = 1 << 0;
+    /** Position view downward from position. */
+    static final int DIRECTION_DOWN = 1 << 1;
+    /** Position view towards the start of the parent. */
+    static final int DIRECTION_START = 1 << 2;
+    /** Position view towards the end of parent. */
+    static final int DIRECTION_END = 1 << 3;
+
+    @Position
+    private final int mPosition;
+
+    @Direction
+    private final int mDirection;
+
+    private final int mWeight;
+
+    // Do not allow specifying opposite positions
+    private static final int[] INVALID_POSITIONS =
+            { POSITION_BOTTOM | POSITION_TOP, POSITION_END | POSITION_START };
+
+    // Do not allow for specifying a direction towards the outside of the container.
+    private static final Map<Integer, Integer> INVALID_DIRECTIONS;
+    static {
+        INVALID_DIRECTIONS = new HashMap<>();
+        INVALID_DIRECTIONS.put(POSITION_BOTTOM, DIRECTION_DOWN);
+        INVALID_DIRECTIONS.put(POSITION_TOP, DIRECTION_UP);
+        INVALID_DIRECTIONS.put(POSITION_START, DIRECTION_START);
+        INVALID_DIRECTIONS.put(POSITION_END, DIRECTION_END);
+    }
+
+    /**
+     * Constructs a {@link ComplicationLayoutParams}.
+     * @param width The width {@link android.view.View.MeasureSpec} for the view.
+     * @param height The height {@link android.view.View.MeasureSpec} for the view.
+     * @param position The place within the parent container where the view should be positioned.
+     * @param direction The direction the view should be laid out from either the parent container
+     *                  or preceding view.
+     * @param weight The weight that should be considered for this view when compared to other
+     *               views. This has an impact on the placement of the view but not the rendering of
+     *               the view.
+     */
+    public ComplicationLayoutParams(int width, int height, @Position int position,
+            @Direction int direction, int weight) {
+        super(width, height);
+
+        if (!validatePosition(position)) {
+            throw new IllegalArgumentException("invalid position:" + position);
+        }
+        mPosition = position;
+
+        if (!validateDirection(position, direction)) {
+            throw new IllegalArgumentException("invalid direction:" + direction);
+        }
+
+        mDirection = direction;
+
+        mWeight = weight;
+    }
+
+    /**
+     * Constructs {@link ComplicationLayoutParams} from an existing instance.
+     */
+    public ComplicationLayoutParams(ComplicationLayoutParams source) {
+        super(source);
+        mPosition = source.mPosition;
+        mDirection = source.mDirection;
+        mWeight = source.mWeight;
+    }
+
+    private static boolean validateDirection(@Position int position, @Direction int direction) {
+        for (int currentPosition = FIRST_POSITION; currentPosition <= LAST_POSITION;
+                currentPosition <<= 1) {
+            if ((position & currentPosition) == currentPosition
+                    && INVALID_DIRECTIONS.containsKey(currentPosition)
+                    && (direction & INVALID_DIRECTIONS.get(currentPosition)) != 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Iterates over the defined positions and invokes the specified {@link Consumer} for each
+     * position specified for this {@link ComplicationLayoutParams}.
+     */
+    public void iteratePositions(Consumer<Integer> consumer) {
+        for (int currentPosition = FIRST_POSITION; currentPosition <= LAST_POSITION;
+                currentPosition <<= 1) {
+            if ((mPosition & currentPosition) == currentPosition) {
+                consumer.accept(currentPosition);
+            }
+        }
+    }
+
+    private static boolean validatePosition(@Position int position) {
+        if (position == 0) {
+            return false;
+        }
+
+        for (int combination : INVALID_POSITIONS) {
+            if ((position & combination) == combination) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Direction
+    public int getDirection() {
+        return mDirection;
+    }
+
+    @Position
+    public int getPosition() {
+        return mPosition;
+    }
+
+    public int getWeight() {
+        return mWeight;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java
new file mode 100644
index 0000000..f023937
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import androidx.lifecycle.ViewModel;
+
+import javax.inject.Inject;
+
+/**
+ * {@link ComplicationViewModel} is an abstraction over {@link Complication}, providing the model
+ * from which any view-related interpretation of the {@link Complication} should be derived from.
+ */
+public class ComplicationViewModel extends ViewModel {
+    private final Complication mComplication;
+    private final ComplicationId mId;
+    private final Complication.Host mHost;
+
+    /**
+     * Default constructor for generating a {@link ComplicationViewModel}.
+     * @param complication The {@link Complication} represented by the view model.
+     * @param id The {@link ComplicationId} tied to this {@link Complication}.
+     * @param host The environment {@link Complication.Host}.
+     */
+    @Inject
+    public ComplicationViewModel(Complication complication, ComplicationId id,
+            Complication.Host host) {
+        mComplication = complication;
+        mId = id;
+        mHost = host;
+    }
+
+    /**
+     * Returns the {@link ComplicationId} for this {@link Complication} for stable id association.
+     */
+    public ComplicationId getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the underlying {@link Complication}. Should only as a redirection - for example,
+     * using the {@link Complication} to generate view. Any property should be surfaced through
+     * this ViewModel.
+     */
+    public Complication getComplication() {
+        return mComplication;
+    }
+
+    /**
+     * Requests the dream exit on behalf of the {@link Complication}.
+     */
+    public void exitDream() {
+        mHost.requestExitDream();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelProvider.java
new file mode 100644
index 0000000..cc17ea1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.lifecycle.ViewModelStore;
+
+import com.android.systemui.dreams.complication.dagger.DaggerViewModelProviderFactory;
+
+import javax.inject.Inject;
+
+/**
+ * An intermediary to generate {@link ComplicationViewModel} tracked with a {@link ViewModelStore}.
+ */
+public class ComplicationViewModelProvider extends ViewModelProvider {
+    @Inject
+    public ComplicationViewModelProvider(ViewModelStore store, ComplicationViewModel viewModel) {
+        super(store, new DaggerViewModelProviderFactory(() -> viewModel));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformer.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformer.java
new file mode 100644
index 0000000..5d113dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import com.android.systemui.dreams.complication.dagger.ComplicationViewModelComponent;
+
+import java.util.HashMap;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link ComplicationViewModelTransformer} responsibility is provide a mapping from
+ * {@link Complication} to {@link ComplicationViewModel}.
+ */
+public class ComplicationViewModelTransformer {
+    private final ComplicationId.Factory mComplicationIdFactory = new ComplicationId.Factory();
+    private final HashMap<Complication, ComplicationId> mComplicationIdMapping = new HashMap<>();
+    private final ComplicationViewModelComponent.Factory mViewModelComponentFactory;
+
+    @Inject
+    public ComplicationViewModelTransformer(
+            ComplicationViewModelComponent.Factory viewModelComponentFactory) {
+        mViewModelComponentFactory = viewModelComponentFactory;
+    }
+
+    /**
+     * Generates {@link ComplicationViewModel} from a {@link Complication}.
+     */
+    public ComplicationViewModel getViewModel(Complication complication) {
+        final ComplicationId id = getComplicationId(complication);
+        return mViewModelComponentFactory.create(complication, id)
+                .getViewModelProvider().get(id.toString(), ComplicationViewModel.class);
+    }
+
+    private ComplicationId getComplicationId(Complication complication) {
+        if (!mComplicationIdMapping.containsKey(complication)) {
+            mComplicationIdMapping.put(complication, mComplicationIdFactory.getNextId());
+        }
+
+        return mComplicationIdMapping.get(complication);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java
new file mode 100644
index 0000000..4cc905e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationHostViewController;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link ComplicationHostViewComponent} encapsulates the shared logic around the host view layer
+ * for complications. Anything that references the layout should be provided through this component
+ * and its child module. The factory should be used in order to best tie the lifetime of the view
+ * to components.
+ */
+@Subcomponent(modules = {
+        ComplicationHostViewComponent.ComplicationHostViewModule.class,
+})
+@ComplicationHostViewComponent.ComplicationHostViewScope
+public interface ComplicationHostViewComponent {
+    String SCOPED_COMPLICATIONS_LAYOUT = "scoped_complications_layout";
+
+    /** Scope annotation for singleton items within {@link ComplicationHostViewComponent}. */
+    @Documented
+    @Retention(RUNTIME)
+    @Scope
+    @interface ComplicationHostViewScope {}
+
+    /**
+     * Factory for generating a new scoped component.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        ComplicationHostViewComponent create();
+    }
+
+    /** */
+    ComplicationHostViewController getController();
+
+    /**
+     * Module for providing a scoped host view.
+     */
+    @Module
+    abstract class ComplicationHostViewModule {
+        /**
+         * Generates a {@link ConstraintLayout}, which can host
+         * {@link com.android.systemui.dreams.complication.Complication} instances.
+         */
+        @Provides
+        @Named(SCOPED_COMPLICATIONS_LAYOUT)
+        @ComplicationHostViewScope
+        static ConstraintLayout providesComplicationHostView(
+                LayoutInflater layoutInflater) {
+            return Preconditions.checkNotNull((ConstraintLayout)
+                            layoutInflater.inflate(R.layout.dream_overlay_complications_layer,
+                                    null),
+                    "R.layout.dream_overlay_complications_layer did not properly inflated");
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.java
new file mode 100644
index 0000000..b29e8c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import androidx.lifecycle.ViewModelProvider;
+import androidx.lifecycle.ViewModelStore;
+
+import com.android.systemui.dreams.complication.ComplicationCollectionViewModel;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module for housing components related to rendering complications.
+ */
+@Module(subcomponents = {
+        ComplicationViewModelComponent.class,
+        ComplicationHostViewComponent.class,
+})
+public interface ComplicationModule {
+    String SCOPED_COMPLICATIONS_MODEL = "scoped_complications_model";
+
+    /** Scope annotation for singleton items within the {@link ComplicationModule}. */
+    @Documented
+    @Retention(RUNTIME)
+    @Scope
+    @interface ComplicationScope {}
+
+    /**
+     * The complication collection is provided through this way to ensure that the instances are
+     * tied to the {@link ViewModelStore}.
+     */
+    @Provides
+    @Named(SCOPED_COMPLICATIONS_MODEL)
+    static ComplicationCollectionViewModel providesComplicationCollectionViewModel(
+            ViewModelStore store, ComplicationCollectionViewModel viewModel) {
+        final ViewModelProvider provider = new ViewModelProvider(store,
+                new DaggerViewModelProviderFactory(() -> viewModel));
+
+        return provider.get(ComplicationCollectionViewModel.class);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationViewModelComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationViewModelComponent.java
new file mode 100644
index 0000000..703cd28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationViewModelComponent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication.dagger;
+
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationId;
+import com.android.systemui.dreams.complication.ComplicationViewModelProvider;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * The {@link ComplicationViewModelComponent} allows for a
+ * {@link com.android.systemui.dreams.complication.ComplicationViewModel} for a particular
+ * {@link Complication}. This component binds these instance specific values to allow injection with
+ * values provided at the wider scope.
+ */
+@Subcomponent
+public interface ComplicationViewModelComponent {
+    /**
+     * Factory for generating {@link ComplicationViewModelComponent}.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        ComplicationViewModelComponent create(@BindsInstance Complication complication,
+                @BindsInstance ComplicationId id);
+    }
+
+    /** */
+    ComplicationViewModelProvider getViewModelProvider();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DaggerViewModelProviderFactory.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DaggerViewModelProviderFactory.java
new file mode 100644
index 0000000..8ffedec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DaggerViewModelProviderFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication.dagger;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+/**
+ * {@link DaggerViewModelProviderFactory} is a wrapper around a lambda allowing for uses of Dagger
+ * component.
+ */
+public class DaggerViewModelProviderFactory implements ViewModelProvider.Factory {
+    /**
+     * An interface for providing a {@link ViewModel} through
+     * {@link DaggerViewModelProviderFactory}.
+     */
+    public interface ViewModelCreator {
+        /**
+         * Creates a {@link ViewModel} to be returned.
+         */
+        ViewModel create();
+    }
+
+    private final ViewModelCreator mCreator;
+
+    public DaggerViewModelProviderFactory(ViewModelCreator creator) {
+        mCreator = creator;
+    }
+
+    @NonNull
+    @Override
+    public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
+        return (T) mCreator.create();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 072f50d..d5053a0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -22,6 +22,7 @@
  * Dagger Module providing Communal-related functionality.
  */
 @Module(subcomponents = {
-        DreamOverlayComponent.class})
+        DreamOverlayComponent.class,
+})
 public interface DreamModule {
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
index c90332b..f0ab696 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -18,25 +18,36 @@
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.ViewModelStore;
+
 import com.android.systemui.dreams.DreamOverlayContainerViewController;
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.dagger.ComplicationModule;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
 import javax.inject.Scope;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
  * Dagger subcomponent for {@link DreamOverlayModule}.
  */
-@Subcomponent(modules = {DreamOverlayModule.class})
+@Subcomponent(modules = {
+        DreamOverlayModule.class,
+        ComplicationModule.class,
+})
 @DreamOverlayComponent.DreamOverlayScope
 public interface DreamOverlayComponent {
     /** Simple factory for {@link DreamOverlayComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        DreamOverlayComponent create();
+        DreamOverlayComponent create(@BindsInstance ViewModelStore store,
+                @BindsInstance Complication.Host host);
     }
 
     /** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
@@ -46,6 +57,11 @@
     @interface DreamOverlayScope {}
 
     /** Builds a {@link DreamOverlayContainerViewController}. */
-    @DreamOverlayScope
     DreamOverlayContainerViewController getDreamOverlayContainerViewController();
+
+    /** Builds a {@link androidx.lifecycle.LifecycleRegistry} */
+    LifecycleRegistry getLifecycleRegistry();
+
+    /** Builds a {@link androidx.lifecycle.LifecycleOwner} */
+    LifecycleOwner getLifecycleOwner();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index d291203..b56aa2c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -22,6 +22,9 @@
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
 import com.android.internal.util.Preconditions;
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
@@ -36,6 +39,7 @@
 
 import javax.inject.Named;
 
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -124,4 +128,16 @@
         return resources.getInteger(
                 R.integer.config_dreamOverlayBurnInProtectionUpdateIntervalMillis);
     }
+
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    static LifecycleOwner providesLifecycleOwner(Lazy<LifecycleRegistry> lifecycleRegistryLazy) {
+        return () -> lifecycleRegistryLazy.get();
+    }
+
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    static LifecycleRegistry providesLifecycleRegistry(LifecycleOwner lifecycleOwner) {
+        return new LifecycleRegistry(lifecycleOwner);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 2a21f42..6cfbb43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -320,7 +320,7 @@
          * @param updatePostTime whether or not to refresh the post time
          */
         public void updateEntry(boolean updatePostTime) {
-            mLogger.logUpdateEntry(updatePostTime);
+            mLogger.logUpdateEntry(mEntry.getKey(), updatePostTime);
 
             long currentTime = mClock.currentTimeMillis();
             mEarliestRemovaltime = currentTime + mMinimumDisplayTime;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f22acb7..0fd9272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -638,22 +638,6 @@
         if (row != null) row.setHeadsUpAnimatingAway(animatingAway);
     }
 
-    /**
-     * Set that this notification was automatically heads upped. This happens for example when
-     * the user bypasses the lockscreen and media is playing.
-     */
-    public void setAutoHeadsUp(boolean autoHeadsUp) {
-        mAutoHeadsUp = autoHeadsUp;
-    }
-
-    /**
-     * @return if this notification was automatically heads upped. This happens for example when
-     *      * the user bypasses the lockscreen and media is playing.
-     */
-    public boolean isAutoHeadsUp() {
-        return mAutoHeadsUp;
-    }
-
     public boolean mustStayOnScreen() {
         return row != null && row.mustStayOnScreen();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index b84b382..0bf21af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -100,7 +100,7 @@
             if (wasHeadsUp) {
                 if (shouldHeadsUp) {
                     mHeadsUpManager.updateNotification(entry.key, hunAgain)
-                } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
+                } else {
                     // We don't want this to be interrupting anymore, let's remove it
                     mHeadsUpManager.removeNotification(
                         entry.key, false /* removeImmediately */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
deleted file mode 100644
index b61a540..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.interruption
-
-import android.content.Context
-import android.media.MediaMetadata
-import android.provider.Settings
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.NotificationMediaManager
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.tuner.TunerService
-import javax.inject.Inject
-
-/**
- * A class that automatically creates heads up for important notification when bypassing the
- * lockscreen
- */
-@SysUISingleton
-class BypassHeadsUpNotifier @Inject constructor(
-    private val context: Context,
-    private val bypassController: KeyguardBypassController,
-    private val statusBarStateController: StatusBarStateController,
-    private val headsUpManager: HeadsUpManagerPhone,
-    private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
-    private val mediaManager: NotificationMediaManager,
-    private val commonNotifCollection: CommonNotifCollection,
-    tunerService: TunerService
-) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
-
-    private var currentMediaEntry: NotificationEntry? = null
-    private var enabled = true
-
-    var fullyAwake = false
-        set(value) {
-            field = value
-            if (value) {
-                updateAutoHeadsUp(currentMediaEntry)
-            }
-        }
-
-    init {
-        statusBarStateController.addCallback(this)
-        tunerService.addTunable(
-                TunerService.Tunable { _, _ ->
-                    enabled = Settings.Secure.getIntForUser(
-                            context.contentResolver,
-                            Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING,
-                            0 /* default */,
-                            KeyguardUpdateMonitor.getCurrentUser()) != 0
-                }, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
-    }
-
-    fun setUp() {
-        mediaManager.addCallback(this)
-    }
-
-    override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
-        val previous = currentMediaEntry
-        val mediaNotificationKey = mediaManager.mediaNotificationKey
-        currentMediaEntry =
-            if (mediaNotificationKey != null && NotificationMediaManager.isPlayingState(state))
-                commonNotifCollection.getEntry(mediaNotificationKey)
-            else null
-        updateAutoHeadsUp(previous)
-        updateAutoHeadsUp(currentMediaEntry)
-    }
-
-    private fun updateAutoHeadsUp(entry: NotificationEntry?) {
-        entry?.let {
-            val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp(it)
-            it.isAutoHeadsUp = autoHeadsUp
-            if (autoHeadsUp) {
-                headsUpManager.showNotification(it)
-            }
-        }
-    }
-
-    /**
-     * @return {@code true} if this entry be autoHeadsUpped right now.
-     */
-    private fun canAutoHeadsUp(entry: NotificationEntry): Boolean {
-        if (!isAutoHeadsUpAllowed()) {
-            return false
-        }
-        if (entry.isSensitive) {
-            // filter sensitive notifications
-            return false
-        }
-        if (!notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) {
-            // filter notifications invisible on Keyguard
-            return false
-        }
-        if (commonNotifCollection.getEntry(entry.key) != null) {
-            // filter notifications not the active list currently
-            return false
-        }
-        return true
-    }
-
-    override fun onStatePostChange() {
-        updateAutoHeadsUp(currentMediaEntry)
-    }
-
-    /**
-     * @return {@code true} if autoHeadsUp is possible right now.
-     */
-    private fun isAutoHeadsUpAllowed(): Boolean {
-        if (!enabled) {
-            return false
-        }
-        if (!bypassController.bypassEnabled) {
-            return false
-        }
-        if (statusBarStateController.state != StatusBarState.KEYGUARD) {
-            return false
-        }
-        if (!fullyAwake) {
-            return false
-        }
-        return true
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
index b1c69e4..74fb3f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
@@ -124,7 +124,7 @@
         if (wasHeadsUp) {
             if (shouldHeadsUp) {
                 mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
-            } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
+            } else {
                 // We don't want this to be interrupting anymore, let's remove it
                 mHeadsUpManager.removeNotification(entry.getKey(), false /* removeImmediately */);
             }
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 63cb4ae..5d6d0f7 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
@@ -424,7 +424,8 @@
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable onFinishRunnable) {
         enableAppearDrawing(true);
         mIsHeadsUpAnimation = isHeadsUpAppear;
         if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 6eff799..8ffdb69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -358,7 +358,12 @@
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener);
 
-    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+        performAddAnimation(delay, duration, isHeadsUpAppear, null);
+    }
+
+    public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable onEndRunnable);
 
     /**
      * Set the notification appearance to be below the speed bump.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
index f26598d..ec406f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
 import com.android.systemui.log.dagger.NotificationLog
 import javax.inject.Inject
 
@@ -50,7 +49,7 @@
     }
 
     fun logRequestPipelineRowNotSet(notifKey: String) {
-        buffer.log(TAG, WARNING, {
+        buffer.log(TAG, INFO, {
             str1 = notifKey
         }, {
             "Row is not set so pipeline will not run. notif = $str1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 3cdaa9a..aa3e027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -242,6 +242,13 @@
     }
 
     @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable endRunnable) {
+        // TODO: use delay and duration
+        setContentVisible(true);
+    }
+
+    @Override
     public boolean needsClippingToShelf() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
index c9a0f6c..bd5b7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
@@ -39,7 +39,8 @@
     }
 
     @Override
-    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable onEnd) {
         // No animation, it doesn't need it, this would be local
     }
 }
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 90f5179..4283343 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
@@ -5658,6 +5658,10 @@
         }
     }
 
+    protected void setLogger(StackStateLogger logger) {
+        mStateAnimator.setLogger(logger);
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 5833ec2..51ce779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -184,6 +184,7 @@
     private final SectionHeaderController mSilentHeaderController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final InteractionJankMonitor mJankMonitor;
+    private final StackStateLogger mStackStateLogger;
 
     private NotificationStackScrollLayout mView;
     private boolean mFadeNotificationsOnDismiss;
@@ -660,7 +661,9 @@
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             ShadeController shadeController,
-            InteractionJankMonitor jankMonitor) {
+            InteractionJankMonitor jankMonitor,
+            StackStateLogger stackLogger) {
+        mStackStateLogger = stackLogger;
         mAllowLongPress = allowLongPress;
         mNotificationGutsManager = notificationGutsManager;
         mVisibilityProvider = visibilityProvider;
@@ -712,6 +715,7 @@
 
     public void attach(NotificationStackScrollLayout view) {
         mView = view;
+        mView.setLogger(mStackStateLogger);
         mView.setController(this);
         mView.setTouchHandler(new TouchHandler());
         mView.setStatusBar(mStatusBar);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 1d0d374..0d2bddc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -86,6 +86,7 @@
     private NotificationShelf mShelf;
     private float mStatusBarIconLocation;
     private int[] mTmpLocation = new int[2];
+    private StackStateLogger mLogger;
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
@@ -113,6 +114,10 @@
         };
     }
 
+    protected void setLogger(StackStateLogger logger) {
+        mLogger = logger;
+    }
+
     public boolean isRunning() {
         return !mAnimatorSet.isEmpty();
     }
@@ -337,6 +342,12 @@
             ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
         for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
             final ExpandableView changingView = (ExpandableView) event.mChangingView;
+            boolean loggable = false;
+            String key = null;
+            if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
+                loggable = true;
+                key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
+            }
             if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
 
@@ -407,10 +418,22 @@
                 if (event.headsUpFromBottom) {
                     mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
                 } else {
+                    Runnable onAnimationEnd = null;
+                    if (loggable) {
+                        String finalKey = key;
+                        onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey);
+                    }
                     changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
-                            true /* isHeadsUpAppear */);
+                            true /* isHeadsUpAppear */, onAnimationEnd);
                 }
                 mHeadsUpAppearChildren.add(changingView);
+                // this only captures HEADS_UP_APPEAR animations, but HUNs can appear with normal
+                // ADD animations, which would not be logged here.
+                if (loggable) {
+                    mLogger.logHUNViewAppearing(
+                            ((ExpandableNotificationRow) changingView).getEntry().getKey());
+                }
+
                 mTmpState.applyToView(changingView);
             } else if (event.animationType == NotificationStackScrollLayout
                             .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
@@ -453,10 +476,21 @@
                     // We need to add the global animation listener, since once no animations are
                     // running anymore, the panel will instantly hide itself. We need to wait until
                     // the animation is fully finished for this though.
+                    Runnable postAnimation = endRunnable;
+                    if (loggable) {
+                        mLogger.logHUNViewDisappearing(key);
+
+                        Runnable finalEndRunnable = endRunnable;
+                        String finalKey1 = key;
+                        postAnimation = () -> {
+                            mLogger.disappearAnimationEnded(finalKey1);
+                            if (finalEndRunnable != null) finalEndRunnable.run();
+                        };
+                    }
                     long removeAnimationDelay = changingView.performRemoveAnimation(
                             ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
                             0, 0.0f, true /* isHeadsUpAppear */, targetLocation,
-                            endRunnable, getGlobalAnimationFinishedListener());
+                            postAnimation, getGlobalAnimationFinishedListener());
                     mAnimationProperties.delay += removeAnimationDelay;
                 } else if (endRunnable != null) {
                     endRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
new file mode 100644
index 0000000..4315265
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.statusbar.notification.stack
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import javax.inject.Inject
+
+class StackStateLogger @Inject constructor(
+    @NotificationHeadsUpLog private val buffer: LogBuffer
+) {
+    fun logHUNViewDisappearing(key: String) {
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = key
+        }, {
+            "Heads up view disappearing $str1 "
+        })
+    }
+
+    fun logHUNViewAppearing(key: String) {
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = key
+        }, {
+            "Heads up notification view appearing $str1 "
+        })
+    }
+
+    fun disappearAnimationEnded(key: String) {
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = key
+        }, {
+            "Heads up notification disappear animation ended $str1 "
+        })
+    }
+
+    fun appearAnimationEnded(key: String) {
+        buffer.log(TAG, LogLevel.INFO, {
+            str1 = key
+        }, {
+            "Heads up notification appear animation ended $str1 "
+        })
+    }
+}
+
+private const val TAG = "StackScroll"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 2824ab8..77cff34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -60,16 +60,14 @@
     private final KeyguardBypassController mBypassController;
     private final GroupMembershipManager mGroupMembershipManager;
     private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
-    private final int mAutoHeadsUpNotificationDecay;
     // TODO (b/162832756): remove visual stability manager when migrating to new pipeline
     private VisualStabilityManager mVisualStabilityManager;
     private boolean mReleaseOnExpandFinish;
 
     private boolean mTrackingHeadsUp;
-    private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private HashSet<String> mKeysToRemoveWhenLeavingKeyguard = new HashSet<>();
-    private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
+    private final HashSet<String> mSwipedOutKeys = new HashSet<>();
+    private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
     private boolean mIsExpanded;
     private boolean mHeadsUpGoingAway;
@@ -110,8 +108,6 @@
         super(context, logger);
         Resources resources = mContext.getResources();
         mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
-        mAutoHeadsUpNotificationDecay = resources.getInteger(
-                R.integer.auto_heads_up_notification_decay);
         statusBarStateController.addCallback(mStatusBarStateListener);
         mBypassController = bypassController;
         mGroupMembershipManager = groupMembershipManager;
@@ -234,15 +230,6 @@
         }
     }
 
-    @Override
-    public boolean isEntryAutoHeadsUpped(String key) {
-        HeadsUpEntryPhone headsUpEntryPhone = getHeadsUpEntryPhone(key);
-        if (headsUpEntryPhone == null) {
-            return false;
-        }
-        return headsUpEntryPhone.isAutoHeadsUp();
-    }
-
     /**
      * Set that we are exiting the headsUp pinned mode, but some notifications might still be
      * animating out. This is used to keep the touchable regions in a reasonable state.
@@ -375,7 +362,6 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.getKey());
         super.onAlertEntryRemoved(alertEntry);
         mEntryPool.release((HeadsUpEntryPhone) alertEntry);
     }
@@ -437,11 +423,6 @@
          */
         private boolean extended;
 
-        /**
-         * Was this entry received while on keyguard
-         */
-        private boolean mIsAutoHeadsUp;
-
 
         @Override
         public boolean isSticky() {
@@ -459,8 +440,6 @@
                             false  /* persistent */);
                 } else if (mTrackingHeadsUp) {
                     mEntriesToRemoveAfterExpand.add(entry);
-                } else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
-                    mKeysToRemoveWhenLeavingKeyguard.add(entry.getKey());
                 } else {
                     removeAlertEntry(entry.getKey());
                 }
@@ -471,7 +450,6 @@
 
         @Override
         public void updateEntry(boolean updatePostTime) {
-            mIsAutoHeadsUp = mEntry.isAutoHeadsUp();
             super.updateEntry(updatePostTime);
 
             if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
@@ -480,7 +458,6 @@
             if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
                 mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
             }
-            mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.getKey());
         }
 
         @Override
@@ -515,7 +492,6 @@
             super.reset();
             mMenuShownPinned = false;
             extended = false;
-            mIsAutoHeadsUp = false;
         }
 
         private void extendPulse() {
@@ -526,33 +502,8 @@
         }
 
         @Override
-        public int compareTo(AlertEntry alertEntry) {
-            HeadsUpEntryPhone headsUpEntry = (HeadsUpEntryPhone) alertEntry;
-            boolean autoShown = isAutoHeadsUp();
-            boolean otherAutoShown = headsUpEntry.isAutoHeadsUp();
-            if (autoShown && !otherAutoShown) {
-                return 1;
-            } else if (!autoShown && otherAutoShown) {
-                return -1;
-            }
-            return super.compareTo(alertEntry);
-        }
-
-        @Override
         protected long calculateFinishTime() {
-            return mPostTime + getDecayDuration() + (extended ? mExtensionTime : 0);
-        }
-
-        private int getDecayDuration() {
-            if (isAutoHeadsUp()) {
-                return getRecommendedHeadsUpTimeoutMs(mAutoHeadsUpNotificationDecay);
-            } else {
-                return getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
-            }
-        }
-
-        private boolean isAutoHeadsUp() {
-            return mIsAutoHeadsUp;
+            return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
         }
     }
 
@@ -577,13 +528,6 @@
             boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
             boolean isKeyguard = newState == StatusBarState.KEYGUARD;
             mStatusBarState = newState;
-            if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
-                String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
-                for (String key : keys) {
-                    removeAlertEntry(key);
-                }
-                mKeysToRemoveWhenLeavingKeyguard.clear();
-            }
             if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
                 ArrayList<String> keysToRemove = new ArrayList<>();
                 for (AlertEntry entry : mAlertEntries.values()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ae4a19e..455ffdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -211,7 +211,6 @@
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -482,7 +481,6 @@
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
     private final DynamicPrivacyController mDynamicPrivacyController;
-    private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
     private final FalsingCollector mFalsingCollector;
     private final FalsingManager mFalsingManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -703,7 +701,6 @@
             KeyguardStateController keyguardStateController,
             HeadsUpManagerPhone headsUpManagerPhone,
             DynamicPrivacyController dynamicPrivacyController,
-            BypassHeadsUpNotifier bypassHeadsUpNotifier,
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             BroadcastDispatcher broadcastDispatcher,
@@ -800,7 +797,6 @@
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
         mDynamicPrivacyController = dynamicPrivacyController;
-        mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
         mFalsingCollector = falsingCollector;
         mFalsingManager = falsingManager;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -917,7 +913,6 @@
         mScreenLifecycle.addObserver(mScreenObserver);
         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
-        mBypassHeadsUpNotifier.setUp();
         if (mBubblesOptional.isPresent()) {
             mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
         }
@@ -3581,7 +3576,6 @@
             maybeEscalateHeadsUp();
             dismissVolumeDialog();
             mWakeUpCoordinator.setFullyAwake(false);
-            mBypassHeadsUpNotifier.setFullyAwake(false);
             mKeyguardBypassController.onStartedGoingToSleep();
 
             // The unlocked screen off and fold to aod animations might use our LightRevealScrim -
@@ -3623,7 +3617,6 @@
         @Override
         public void onFinishedWakingUp() {
             mWakeUpCoordinator.setFullyAwake(true);
-            mBypassHeadsUpNotifier.setFullyAwake(true);
             mWakeUpCoordinator.setWakingUp(false);
             if (mLaunchCameraWhenFinishedWaking) {
                 mNotificationPanelViewController.launchCamera(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 977fe9c..f5364b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -71,7 +71,6 @@
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -152,7 +151,6 @@
             KeyguardStateController keyguardStateController,
             HeadsUpManagerPhone headsUpManagerPhone,
             DynamicPrivacyController dynamicPrivacyController,
-            BypassHeadsUpNotifier bypassHeadsUpNotifier,
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             BroadcastDispatcher broadcastDispatcher,
@@ -250,7 +248,6 @@
                 keyguardStateController,
                 headsUpManagerPhone,
                 dynamicPrivacyController,
-                bypassHeadsUpNotifier,
                 falsingManager,
                 falsingCollector,
                 broadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 9587261..3084a95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -184,6 +184,7 @@
         entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
+        mLogger.logNotificationActuallyRemoved(entry.getKey());
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
@@ -378,10 +379,6 @@
     public void onDensityOrFontScaleChanged() {
     }
 
-    public boolean isEntryAutoHeadsUpped(String key) {
-        return false;
-    }
-
     /**
      * Determines if the notification is for a critical call that must display on top of an active
      * input notification.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 2bdf62bb..6a74ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -73,6 +73,14 @@
         })
     }
 
+    fun logNotificationActuallyRemoved(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "notification removed $str1 "
+        })
+    }
+
     fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
         buffer.log(TAG, INFO, {
             str1 = key
@@ -83,11 +91,12 @@
         })
     }
 
-    fun logUpdateEntry(updatePostTime: Boolean) {
+    fun logUpdateEntry(key: String, updatePostTime: Boolean) {
         buffer.log(TAG, INFO, {
+            str1 = key
             bool1 = updatePostTime
         }, {
-            "update entry updatePostTime: $bool1"
+            "update entry $key updatePostTime: $bool1"
         })
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
deleted file mode 100644
index ada7ddb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 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.dreams;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.settingslib.dream.DreamBackend;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ComplicationProviderTest {
-    private TestComplicationProvider mComplicationProvider;
-
-    @Before
-    public void setup() {
-        mComplicationProvider = new TestComplicationProvider();
-    }
-
-    @Test
-    public void testConvertComplicationType() {
-        assertEquals(ComplicationProvider.COMPLICATION_TYPE_TIME,
-                mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME));
-        assertEquals(ComplicationProvider.COMPLICATION_TYPE_DATE,
-                mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE));
-        assertEquals(ComplicationProvider.COMPLICATION_TYPE_WEATHER,
-                mComplicationProvider.convertComplicationType(
-                        DreamBackend.COMPLICATION_TYPE_WEATHER));
-        assertEquals(ComplicationProvider.COMPLICATION_TYPE_AIR_QUALITY,
-                mComplicationProvider.convertComplicationType(
-                        DreamBackend.COMPLICATION_TYPE_AIR_QUALITY));
-        assertEquals(ComplicationProvider.COMPLICATION_TYPE_CAST_INFO,
-                mComplicationProvider.convertComplicationType(
-                        DreamBackend.COMPLICATION_TYPE_CAST_INFO));
-    }
-
-    private static class TestComplicationProvider implements ComplicationProvider {
-        @Override
-        public void onCreateComplication(Context context,
-                ComplicationHost.CreationCallback creationCallback,
-                ComplicationHost.InteractionCallback interactionCallback) {
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 7c5f57f..7af039b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -27,15 +27,15 @@
 import android.content.res.Resources;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
-import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -64,6 +64,15 @@
     DreamOverlayContainerView mDreamOverlayContainerView;
 
     @Mock
+    ComplicationHostViewController mComplicationHostViewController;
+
+    @Mock
+    ComplicationHostViewComponent.Factory mComplicationHostViewComponentFactory;
+
+    @Mock
+    ComplicationHostViewComponent mComplicationHostViewComponent;
+
+    @Mock
     ViewGroup mDreamOverlayContentView;
 
     @Mock
@@ -80,9 +89,14 @@
                         DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT);
         when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
         when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+        when(mComplicationHostViewComponentFactory.create())
+                .thenReturn(mComplicationHostViewComponent);
+        when(mComplicationHostViewComponent.getController())
+                .thenReturn(mComplicationHostViewController);
 
         mController = new DreamOverlayContainerViewController(
                 mDreamOverlayContainerView,
+                mComplicationHostViewComponentFactory,
                 mDreamOverlayContentView,
                 mDreamOverlayStatusBarViewController,
                 mHandler,
@@ -104,20 +118,6 @@
     }
 
     @Test
-    public void testAddOverlayAddsOverlayToContentView() {
-        View overlay = new View(getContext());
-        ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(100, 100);
-        mController.addOverlay(overlay, layoutParams);
-        verify(mDreamOverlayContentView).addView(overlay, layoutParams);
-    }
-
-    @Test
-    public void testRemoveAllOverlaysRemovesOverlaysFromContentView() {
-        mController.removeAllOverlays();
-        verify(mDreamOverlayContentView).removeAllViews();
-    }
-
-    @Test
     public void testOnViewAttachedRegistersComputeInsetsListener() {
         mController.onViewAttached();
         verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index c0b7271..6b156a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -28,12 +28,11 @@
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
 import android.testing.AndroidTestingRunner;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
-import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
@@ -48,18 +47,21 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class DreamOverlayServiceTest extends SysuiTestCase {
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
 
+    @Mock
+    LifecycleOwner mLifecycleOwner;
+
+    @Mock
+    LifecycleRegistry mLifecycleRegistry;
+
     @Rule
     public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
 
@@ -76,12 +78,6 @@
     WindowManagerImpl mWindowManager;
 
     @Mock
-    ComplicationProvider mProvider;
-
-    @Mock
-    DreamOverlayStateController mDreamOverlayStateController;
-
-    @Mock
     DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
 
     @Mock
@@ -102,13 +98,18 @@
 
         when(mDreamOverlayComponent.getDreamOverlayContainerViewController())
                 .thenReturn(mDreamOverlayContainerViewController);
-        when(mDreamOverlayComponentFactory.create())
+        when(mDreamOverlayComponent.getLifecycleOwner())
+                .thenReturn(mLifecycleOwner);
+        when(mDreamOverlayComponent.getLifecycleRegistry())
+                .thenReturn(mLifecycleRegistry);
+        when(mDreamOverlayComponentFactory
+                .create(any(), any()))
                 .thenReturn(mDreamOverlayComponent);
         when(mDreamOverlayContainerViewController.getContainerView())
                 .thenReturn(mDreamOverlayContainerView);
 
         mService = new DreamOverlayService(mContext, mMainExecutor,
-                mDreamOverlayStateController, mDreamOverlayComponentFactory);
+                mDreamOverlayComponentFactory);
         final IBinder proxy = mService.onBind(new Intent());
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
@@ -128,78 +129,6 @@
     }
 
     @Test
-    public void testAddingOverlayToDream() throws Exception {
-        // Add overlay.
-        mService.addComplication(mProvider);
-        mMainExecutor.runAllReady();
-
-        final ArgumentCaptor<ComplicationHost.CreationCallback> creationCallbackCapture =
-                ArgumentCaptor.forClass(ComplicationHost.CreationCallback.class);
-        final ArgumentCaptor<ComplicationHost.InteractionCallback> interactionCallbackCapture =
-                ArgumentCaptor.forClass(ComplicationHost.InteractionCallback.class);
-
-        // Ensure overlay provider is asked to create view.
-        verify(mProvider).onCreateComplication(any(), creationCallbackCapture.capture(),
-                interactionCallbackCapture.capture());
-        mMainExecutor.runAllReady();
-
-        // Inform service of overlay view creation.
-        final View view = new View(mContext);
-        final ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
-        );
-        creationCallbackCapture.getValue().onCreated(view, lp);
-        mMainExecutor.runAllReady();
-
-        // Verify that DreamOverlayContainerViewController is asked to add an overlay for the view.
-        verify(mDreamOverlayContainerViewController).addOverlay(view, lp);
-    }
-
-    @Test
-    public void testDreamOverlayExit() throws Exception {
-        // Add overlay.
-        mService.addComplication(mProvider);
-        mMainExecutor.runAllReady();
-
-        // Capture interaction callback from overlay creation.
-        final ArgumentCaptor<ComplicationHost.InteractionCallback> interactionCallbackCapture =
-                ArgumentCaptor.forClass(ComplicationHost.InteractionCallback.class);
-        verify(mProvider).onCreateComplication(any(), any(), interactionCallbackCapture.capture());
-
-        // Ask service to exit.
-        interactionCallbackCapture.getValue().onExit();
-        mMainExecutor.runAllReady();
-
-        // Ensure service informs dream host of exit.
-        verify(mDreamOverlayCallback).onExitRequested();
-    }
-
-    @Test
-    public void testListenerRegisteredWithDreamOverlayStateController() {
-        // Verify overlay service registered as listener with DreamOverlayStateController
-        // and inform callback of addition.
-        final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
-                ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
-
-        verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
-        when(mDreamOverlayStateController.getComplications()).thenReturn(Arrays.asList(mProvider));
-        callbackCapture.getValue().onComplicationsChanged();
-        mMainExecutor.runAllReady();
-
-        // Verify provider is asked to create overlay.
-        verify(mProvider).onCreateComplication(any(), any(), any());
-    }
-
-    @Test
-    public void testOnDestroyRemovesOverlayStateCallback() {
-        final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
-                ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
-        verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
-        mService.onDestroy();
-        verify(mDreamOverlayStateController).removeCallback(callbackCapture.getValue());
-    }
-
-    @Test
     public void testShouldShowComplicationsTrueByDefault() {
         assertThat(mService.shouldShowComplications()).isTrue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index efc3c7c..7d0833d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -27,11 +27,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.Complication;
 import com.android.systemui.util.concurrency.FakeExecutor;
 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;
@@ -47,7 +46,7 @@
     DreamOverlayStateController.Callback mCallback;
 
     @Mock
-    ComplicationProvider mProvider;
+    Complication mComplication;
 
     final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -63,24 +62,23 @@
         stateController.addCallback(mCallback);
 
         // Add complication and verify callback is notified.
-        final ListenableFuture<DreamOverlayStateController.ComplicationToken> tokenFuture =
-                stateController.addComplication(mProvider);
+        stateController.addComplication(mComplication);
 
         mExecutor.runAllReady();
 
         verify(mCallback, times(1)).onComplicationsChanged();
 
-        final Collection<ComplicationProvider> providers = stateController.getComplications();
-        assertEquals(providers.size(), 1);
-        assertTrue(providers.contains(mProvider));
+        final Collection<Complication> complications = stateController.getComplications();
+        assertEquals(complications.size(), 1);
+        assertTrue(complications.contains(mComplication));
 
         clearInvocations(mCallback);
 
         // Remove complication and verify callback is notified.
-        stateController.removeComplication(tokenFuture.get());
+        stateController.removeComplication(mComplication);
         mExecutor.runAllReady();
         verify(mCallback, times(1)).onComplicationsChanged();
-        assertTrue(providers.isEmpty());
+        assertTrue(stateController.getComplications().isEmpty());
     }
 
     @Test
@@ -88,7 +86,7 @@
         final DreamOverlayStateController stateController =
                 new DreamOverlayStateController(mExecutor);
 
-        stateController.addComplication(mProvider);
+        stateController.addComplication(mComplication);
         mExecutor.runAllReady();
 
         // Verify callback occurs on add when an overlay is already present.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
new file mode 100644
index 0000000..afc0309d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.lifecycle.Observer;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
+    @Before
+    public void setUp() throws Exception {
+        allowTestableLooperAsMainThread();
+    }
+
+    @Test
+    /**
+     * Ensures registration and callback lifecycles are respected.
+     */
+    public void testLifecycle() {
+        getContext().getMainExecutor().execute(() -> {
+            final DreamOverlayStateController stateController =
+                    Mockito.mock(DreamOverlayStateController.class);
+            final ComplicationCollectionLiveData liveData =
+                    new ComplicationCollectionLiveData(stateController);
+            final HashSet<Complication> complications = new HashSet<>();
+            final Observer<Collection<Complication>> observer = Mockito.mock(Observer.class);
+            complications.add(Mockito.mock(Complication.class));
+
+            when(stateController.getComplications()).thenReturn(complications);
+
+            liveData.observeForever(observer);
+            ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
+                    ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+
+            verify(stateController).addCallback(callbackCaptor.capture());
+            verifyUpdate(observer, complications);
+
+            complications.add(Mockito.mock(Complication.class));
+            callbackCaptor.getValue().onComplicationsChanged();
+
+            verifyUpdate(observer, complications);
+        });
+    }
+
+    void verifyUpdate(Observer<Collection<Complication>> observer,
+            Collection<Complication> targetCollection) {
+        ArgumentCaptor<Collection<Complication>> collectionCaptor =
+                ArgumentCaptor.forClass(Collection.class);
+
+        verify(observer).onChanged(collectionCaptor.capture());
+
+        assertThat(collectionCaptor.getValue().equals(targetCollection)).isTrue();
+        Mockito.clearInvocations(observer);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
new file mode 100644
index 0000000..3b9e398
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationHostViewControllerTest extends SysuiTestCase {
+    @Mock
+    ConstraintLayout mComplicationHostView;
+
+    @Mock
+    LifecycleOwner mLifecycleOwner;
+
+    @Mock
+    LiveData<Collection<ComplicationViewModel>> mComplicationViewModelLiveData;
+
+    @Mock
+    ComplicationCollectionViewModel mViewModel;
+
+    @Mock
+    ComplicationViewModel mComplicationViewModel;
+
+    @Mock
+    ComplicationLayoutEngine mLayoutEngine;
+
+    @Mock
+    ComplicationId mComplicationId;
+
+    @Mock
+    Complication mComplication;
+
+    @Mock
+    Complication.ViewHolder mViewHolder;
+
+    @Mock
+    View mComplicationView;
+
+    @Mock
+    ComplicationLayoutParams mComplicationLayoutParams;
+
+    @Complication.Category
+    static final int COMPLICATION_CATEGORY = Complication.CATEGORY_SYSTEM;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mViewModel.getComplications()).thenReturn(mComplicationViewModelLiveData);
+        when(mComplicationViewModel.getId()).thenReturn(mComplicationId);
+        when(mComplicationViewModel.getComplication()).thenReturn(mComplication);
+        when(mComplication.createView(eq(mComplicationViewModel))).thenReturn(mViewHolder);
+        when(mViewHolder.getView()).thenReturn(mComplicationView);
+        when(mViewHolder.getCategory()).thenReturn(COMPLICATION_CATEGORY);
+        when(mViewHolder.getLayoutParams()).thenReturn(mComplicationLayoutParams);
+        when(mComplicationView.getParent()).thenReturn(mComplicationHostView);
+    }
+
+    /**
+     * Ensures the lifecycle of complications is properly handled.
+     */
+    @Test
+    public void testViewModelObservation() {
+        final ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> observerArgumentCaptor =
+                ArgumentCaptor.forClass(Observer.class);
+        final ComplicationHostViewController controller = new ComplicationHostViewController(
+                mComplicationHostView,
+                mLayoutEngine,
+                mLifecycleOwner,
+                mViewModel);
+
+        controller.init();
+
+        verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
+                observerArgumentCaptor.capture());
+
+        final Observer<Collection<ComplicationViewModel>> observer =
+                observerArgumentCaptor.getValue();
+
+        // Add complication and ensure it is added to the view.
+        final HashSet<ComplicationViewModel> complications = new HashSet<>(
+                Arrays.asList(mComplicationViewModel));
+        observer.onChanged(complications);
+
+        verify(mLayoutEngine).addComplication(eq(mComplicationId), eq(mComplicationView),
+                eq(mComplicationLayoutParams), eq(COMPLICATION_CATEGORY));
+
+        // Remove complication and ensure it is removed from the view by id.
+        observer.onChanged(new HashSet<>());
+
+        verify(mLayoutEngine).removeComplication(eq(mComplicationId));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
new file mode 100644
index 0000000..f227a9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+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.function.Consumer;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationLayoutEngineTest extends SysuiTestCase {
+    @Mock
+    ConstraintLayout mLayout;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private static class ViewInfo {
+        private static int sNextId = 1;
+        public final ComplicationId id;
+        public final View view;
+        public final ComplicationLayoutParams lp;
+
+        @Complication.Category
+        public final int category;
+
+        private static ComplicationId.Factory sFactory = new ComplicationId.Factory();
+
+        ViewInfo(ComplicationLayoutParams params, @Complication.Category int category,
+                ConstraintLayout layout) {
+            this.lp = params;
+            this.category = category;
+            this.view = Mockito.mock(View.class);
+            this.id = sFactory.getNextId();
+            when(view.getId()).thenReturn(sNextId++);
+            when(view.getParent()).thenReturn(layout);
+        }
+
+        void clearInvocations() {
+            Mockito.clearInvocations(view);
+        }
+    }
+
+    private void verifyChange(ViewInfo viewInfo,
+            boolean verifyAdd,
+            Consumer<ConstraintLayout.LayoutParams> paramConsumer) {
+        ArgumentCaptor<ConstraintLayout.LayoutParams> lpCaptor =
+                ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
+        verify(viewInfo.view).setLayoutParams(lpCaptor.capture());
+
+        if (verifyAdd) {
+            verify(mLayout).addView(eq(viewInfo.view));
+        }
+
+        ConstraintLayout.LayoutParams capturedParams = lpCaptor.getValue();
+        paramConsumer.accept(capturedParams);
+    }
+
+    private void addComplication(ComplicationLayoutEngine engine, ViewInfo info) {
+        engine.addComplication(info.id, info.view, info.lp, info.category);
+    }
+
+    /**
+     * Makes sure the engine properly places a view within the {@link ConstraintLayout}.
+     */
+    @Test
+    public void testSingleLayout() {
+        final ViewInfo firstViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                        | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+        addComplication(engine, firstViewInfo);
+
+        // Ensure the view is added to the top end corner
+        verifyChange(firstViewInfo, true, lp -> {
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+    }
+
+    /**
+     * Ensures layout in a particular direction updates.
+     */
+    @Test
+    public void testDirectionLayout() {
+        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+
+        final ViewInfo firstViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        addComplication(engine, firstViewInfo);
+
+        firstViewInfo.clearInvocations();
+
+        final ViewInfo secondViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        addComplication(engine, secondViewInfo);
+
+        // The first added view should now be underneath the second view.
+        verifyChange(firstViewInfo, false, lp -> {
+            assertThat(lp.topToBottom == secondViewInfo.view.getId()).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+
+        // The second view should be in the top position.
+        verifyChange(secondViewInfo, true, lp -> {
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+    }
+
+    /**
+     * Ensures layout in a particular position updates.
+     */
+    @Test
+    public void testPositionLayout() {
+        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+
+        final ViewInfo firstViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        addComplication(engine, firstViewInfo);
+
+        final ViewInfo secondViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        addComplication(engine, secondViewInfo);
+
+        firstViewInfo.clearInvocations();
+        secondViewInfo.clearInvocations();
+
+        final ViewInfo thirdViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_START,
+                        1),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        addComplication(engine, thirdViewInfo);
+
+        // The first added view should now be underneath the second view.
+        verifyChange(firstViewInfo, false, lp -> {
+            assertThat(lp.topToBottom == secondViewInfo.view.getId()).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+
+        // The second view should be in underneath the third view.
+        verifyChange(secondViewInfo, false, lp -> {
+            assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+
+        // The third view should be in at the top.
+        verifyChange(thirdViewInfo, true, lp -> {
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+
+        final ViewInfo fourthViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_START,
+                        1),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        addComplication(engine, fourthViewInfo);
+
+        verifyChange(fourthViewInfo, true, lp -> {
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
+        });
+    }
+
+    /**
+     * Ensures layout in a particular position updates.
+     */
+    @Test
+    public void testRemoval() {
+        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+
+        final ViewInfo firstViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_STANDARD,
+                mLayout);
+
+        engine.addComplication(firstViewInfo.id, firstViewInfo.view, firstViewInfo.lp,
+                firstViewInfo.category);
+
+        final ViewInfo secondViewInfo = new ViewInfo(
+                new ComplicationLayoutParams(
+                        100,
+                        100,
+                        ComplicationLayoutParams.POSITION_TOP
+                                | ComplicationLayoutParams.POSITION_END,
+                        ComplicationLayoutParams.DIRECTION_DOWN,
+                        0),
+                Complication.CATEGORY_SYSTEM,
+                mLayout);
+
+        engine.addComplication(secondViewInfo.id, secondViewInfo.view, secondViewInfo.lp,
+                secondViewInfo.category);
+
+        firstViewInfo.clearInvocations();
+
+        engine.removeComplication(secondViewInfo.id);
+        verify(mLayout).removeView(eq(secondViewInfo.view));
+
+        verifyChange(firstViewInfo, true, lp -> {
+            assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+            assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+        });
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
new file mode 100644
index 0000000..d080bbc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationLayoutParamsTest extends SysuiTestCase {
+    /**
+     * Ensures ComplicationLayoutParams cannot be constructed with improper position or direction.
+     */
+    @Test
+    public void testPositionValidation() {
+        final HashSet<Integer> invalidCombinations = new HashSet(Arrays.asList(
+                ComplicationLayoutParams.POSITION_BOTTOM | ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.POSITION_END | ComplicationLayoutParams.POSITION_START
+        ));
+
+        final int allPositions = ComplicationLayoutParams.POSITION_TOP
+                | ComplicationLayoutParams.POSITION_START
+                | ComplicationLayoutParams.POSITION_END
+                | ComplicationLayoutParams.POSITION_BOTTOM;
+
+        final HashSet<Integer> allDirections = new HashSet(Arrays.asList(
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                ComplicationLayoutParams.DIRECTION_UP,
+                ComplicationLayoutParams.DIRECTION_START,
+                ComplicationLayoutParams.DIRECTION_END
+        ));
+
+        final HashMap<Integer, Integer> invalidDirections = new HashMap<>();
+        invalidDirections.put(ComplicationLayoutParams.DIRECTION_DOWN,
+                ComplicationLayoutParams.POSITION_BOTTOM);
+        invalidDirections.put(ComplicationLayoutParams.DIRECTION_UP,
+                ComplicationLayoutParams.POSITION_TOP);
+        invalidDirections.put(ComplicationLayoutParams.DIRECTION_START,
+                ComplicationLayoutParams.POSITION_START);
+        invalidDirections.put(ComplicationLayoutParams.DIRECTION_END,
+                ComplicationLayoutParams.POSITION_END);
+
+
+        for (int position = 0; position <= allPositions; ++position) {
+            boolean properPosition = position != 0;
+            if (properPosition) {
+                for (Integer combination : invalidCombinations) {
+                    if ((combination & position) == combination) {
+                        properPosition = false;
+                    }
+                }
+            }
+            boolean exceptionEncountered = false;
+            for (Integer direction : allDirections) {
+                final int invalidPosition = invalidDirections.get(direction);
+                final boolean properDirection = (invalidPosition & position) != invalidPosition;
+
+                try {
+                    final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                            100,
+                            100,
+                            position,
+                            direction,
+                            0);
+                } catch (Exception e) {
+                    exceptionEncountered = true;
+                }
+
+                assertThat((properPosition && properDirection) || exceptionEncountered).isTrue();
+            }
+        }
+    }
+
+    /**
+     * Ensures ComplicationLayoutParams is properly duplicated on copy construction.
+     */
+    @Test
+    public void testCopyConstruction() {
+        final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                100,
+                100,
+                ComplicationLayoutParams.POSITION_TOP,
+                ComplicationLayoutParams.DIRECTION_DOWN,
+                3);
+        final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
+
+        assertThat(copy.getDirection() == params.getDirection()).isTrue();
+        assertThat(copy.getPosition() == params.getPosition()).isTrue();
+        assertThat(copy.getWeight() == params.getWeight()).isTrue();
+        assertThat(copy.height == params.height).isTrue();
+        assertThat(copy.width == params.width).isTrue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformerTest.java
new file mode 100644
index 0000000..2bc427d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationViewModelTransformerTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.dagger.ComplicationViewModelComponent;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationViewModelTransformerTest extends SysuiTestCase {
+    @Mock
+    ComplicationViewModelComponent.Factory mFactory;
+
+    @Mock
+    ComplicationViewModelComponent mComponent;
+
+    @Mock
+    ComplicationViewModelProvider mViewModelProvider;
+
+    @Mock
+    ComplicationViewModel mViewModel;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mFactory.create(Mockito.any(), Mockito.any())).thenReturn(mComponent);
+        when(mComponent.getViewModelProvider()).thenReturn(mViewModelProvider);
+        when(mViewModelProvider.get(Mockito.any(), Mockito.any())).thenReturn(mViewModel);
+    }
+
+    /**
+     * Ensure the same id is returned for the same complication across invocations.
+     */
+    @Test
+    public void testStableIds() {
+        final ComplicationViewModelTransformer transformer =
+                new ComplicationViewModelTransformer(mFactory);
+
+        final Complication complication = Mockito.mock(Complication.class);
+
+        ArgumentCaptor<ComplicationId> idCaptor = ArgumentCaptor.forClass(ComplicationId.class);
+
+        transformer.getViewModel(complication);
+        verify(mFactory).create(Mockito.any(), idCaptor.capture());
+        final ComplicationId firstId = idCaptor.getValue();
+
+        Mockito.clearInvocations(mFactory);
+
+        transformer.getViewModel(complication);
+        verify(mFactory).create(Mockito.any(), idCaptor.capture());
+        final ComplicationId secondId = idCaptor.getValue();
+
+        assertEquals(secondId, firstId);
+    }
+
+    /**
+     * Ensure unique ids are assigned to different complications.
+     */
+    @Test
+    public void testUniqueIds() {
+        final ComplicationViewModelTransformer transformer =
+                new ComplicationViewModelTransformer(mFactory);
+
+        final Complication firstComplication = Mockito.mock(Complication.class);
+        final Complication secondComplication = Mockito.mock(Complication.class);
+
+        ArgumentCaptor<ComplicationId> idCaptor = ArgumentCaptor.forClass(ComplicationId.class);
+
+        transformer.getViewModel(firstComplication);
+        verify(mFactory).create(Mockito.any(), idCaptor.capture());
+        final ComplicationId firstId = idCaptor.getValue();
+
+        Mockito.clearInvocations(mFactory);
+
+        transformer.getViewModel(secondComplication);
+        verify(mFactory).create(Mockito.any(), idCaptor.capture());
+        final ComplicationId secondId = idCaptor.getValue();
+
+        assertNotEquals(secondId, firstId);
+    }
+}
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
index a1ec38f..81ae209 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.util.mockito.capture
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.mockito.Mock
 import org.mockito.Mockito.anyString
@@ -42,6 +43,7 @@
 import java.util.concurrent.Executor
 
 @SmallTest
+@Ignore("b/216286227")
 class MediaTttCommandLineHelperTest : SysuiTestCase() {
 
     private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index 927ca7a..242fd19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -38,6 +39,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@Ignore("b/216286227")
 class MediaTttChipControllerCommonTest : SysuiTestCase() {
     private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index afaab80..1d1265b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -34,6 +35,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@Ignore("b/216286227")
 class MediaTttChipControllerReceiverTest : SysuiTestCase() {
     private lateinit var controllerReceiver: MediaTttChipControllerReceiver
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 509ae33..6b4eebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -37,6 +38,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@Ignore("b/216286227")
 class MediaTttChipControllerSenderTest : SysuiTestCase() {
     private lateinit var appIconDrawable: Drawable
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
index 11b727e..64542cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -11,12 +11,14 @@
 import com.android.systemui.util.mockito.capture
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
+@Ignore("b/216286227")
 class MediaTttSenderServiceTest : SysuiTestCase() {
 
     private lateinit var service: IDeviceSenderService
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 04c6f6c..86a705f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -137,6 +137,7 @@
     @Mock private VisualStabilityManager mVisualStabilityManager;
     @Mock private ShadeController mShadeController;
     @Mock private InteractionJankMonitor mJankMonitor;
+    @Mock private StackStateLogger mStackLogger;
 
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -191,7 +192,8 @@
                 mRemoteInputManager,
                 mVisualStabilityManager,
                 mShadeController,
-                mJankMonitor
+                mJankMonitor,
+                mStackLogger
         );
 
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 77065b2..f4f5bfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -129,7 +129,6 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
@@ -226,7 +225,6 @@
     @Mock private NotificationMediaManager mNotificationMediaManager;
     @Mock private NavigationBarController mNavigationBarController;
     @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
-    @Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
     @Mock private SysuiColorExtractor mColorExtractor;
     @Mock private ColorExtractor.GradientColors mGradientColors;
     @Mock private PulseExpansionHandler mPulseExpansionHandler;
@@ -394,7 +392,6 @@
                 mKeyguardStateController,
                 mHeadsUpManager,
                 mDynamicPrivacyController,
-                mBypassHeadsUpNotifier,
                 new FalsingManagerFake(),
                 new FalsingCollectorFake(),
                 mBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index d15ba26..d325840 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -23,14 +23,20 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
 
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -40,12 +46,14 @@
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -58,10 +66,15 @@
     private HeadsUpManager mHeadsUpManager;
     private boolean mLivesPastNormalTime;
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
+    @Mock private HeadsUpManager.HeadsUpEntry mAlertEntry;
+    @Mock private NotificationEntry mEntry;
+    @Mock private StatusBarNotification mSbn;
+    @Mock private Notification mNotification;
+    @Mock private HeadsUpManagerLogger mLogger;
 
     private final class TestableHeadsUpManager extends HeadsUpManager {
-        TestableHeadsUpManager(Context context) {
-            super(context, mock(HeadsUpManagerLogger.class));
+        TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger) {
+            super(context, logger);
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
         }
@@ -73,10 +86,12 @@
 
     @Before
     public void setUp() {
+        initMocks(this);
         mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
         mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake);
-
-        mHeadsUpManager = new TestableHeadsUpManager(mContext);
+        when(mEntry.getSbn()).thenReturn(mSbn);
+        when(mSbn.getNotification()).thenReturn(mNotification);
+        mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger);
         super.setUp();
         mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
         mHeadsUpManager.mHandler = mTestHandler;
@@ -88,6 +103,13 @@
     }
 
     @Test
+    public void testHunRemovedLogging() {
+        mAlertEntry.mEntry = mEntry;
+        mHeadsUpManager.onAlertEntryRemoved(mAlertEntry);
+        verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(mEntry.getKey()));
+    }
+
+    @Test
     public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
         doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
                 .getRecommendedTimeoutMillis(anyInt(), anyInt());
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0f35456..51b49ed 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -516,6 +516,8 @@
                 .append(String.valueOf(mMagnificationCapabilities));
         pw.append(", audioDescriptionByDefaultEnabled=")
                 .append(String.valueOf(mIsAudioDescriptionByDefaultRequested));
+        pw.append(", magnificationFollowTypingEnabled=")
+                .append(String.valueOf(mMagnificationFollowTypingEnabled));
         pw.append("}");
         pw.println();
         pw.append("     shortcut key:{");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index e39b979..fe97a46 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -769,6 +769,10 @@
         mMagnificationFollowTypingEnabled = enabled;
     }
 
+    boolean isMagnificationFollowTypingEnabled() {
+        return mMagnificationFollowTypingEnabled;
+    }
+
     /**
      * Remove the display magnification with given id.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 77e3ee5..9eb77b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -365,13 +365,18 @@
         mController.getFullScreenMagnificationController().unregister(displayId);
     }
 
-    /** Dumps {@link MagnificationConfig} and magnification region of magnifiers on the displays. */
+    /**
+     * Dumps magnification configuration {@link MagnificationConfig} and state for each
+     * {@link Display}
+     */
     public void dump(final PrintWriter pw, ArrayList<Display> displaysList) {
         for (int i = 0; i < displaysList.size(); i++) {
             final int displayId = displaysList.get(i).getDisplayId();
+
             final MagnificationConfig config = getMagnificationConfig(displayId);
             pw.println("Magnifier on display#" + displayId);
             pw.append("    " + config).println();
+
             final Region region = new Region();
             getCurrentMagnificationRegion(displayId, region, true);
             if (!region.isEmpty()) {
@@ -379,6 +384,8 @@
             }
             pw.append("    IdOfLastServiceToMagnify="
                     + getIdOfLastServiceToMagnify(config.getMode(), displayId)).println();
+
+            dumpTrackingTypingFocusEnabledState(pw, displayId, config.getMode());
         }
     }
 
@@ -389,4 +396,13 @@
                 : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
                         displayId);
     }
+
+    private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
+            int mode) {
+        if (mode == MAGNIFICATION_MODE_WINDOW) {
+            pw.append("    TrackingTypingFocusEnabled="  + mController
+                            .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
+                    .println();
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 2fbefa4..278f3f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -318,6 +318,10 @@
         mMagnificationFollowTypingEnabled = enabled;
     }
 
+    boolean isMagnificationFollowTypingEnabled() {
+        return mMagnificationFollowTypingEnabled;
+    }
+
     /**
      * Get the ID of the last service that changed the magnification config.
      *
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 5c8fb2e..95b9e58 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -45,10 +45,12 @@
 import android.hardware.input.VirtualTouchEvent;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -59,6 +61,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Map;
 import java.util.Set;
 
 
@@ -78,6 +81,7 @@
     private final OnDeviceCloseListener mListener;
     private final IBinder mAppToken;
     private final VirtualDeviceParams mParams;
+    private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
     private final IVirtualDeviceActivityListener mActivityListener;
 
     private ActivityListener createListenerAdapter(int displayId) {
@@ -206,6 +210,16 @@
 
     @Override // Binder call
     public void close() {
+        synchronized (mVirtualDeviceLock) {
+            if (!mPerDisplayWakelocks.isEmpty()) {
+                mPerDisplayWakelocks.forEach((displayId, wakeLock) -> {
+                    Slog.w(TAG, "VirtualDisplay " + displayId + " owned by UID " + mOwnerUid
+                            + " was not properly released");
+                    wakeLock.release();
+                });
+                mPerDisplayWakelocks.clear();
+            }
+        }
         mListener.onClose(mAssociationInfo.getId());
         mAppToken.unlinkToDeath(this, 0);
         mInputController.close();
@@ -383,22 +397,48 @@
     }
 
     DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) {
-        if (mVirtualDisplayIds.contains(displayId)) {
-            throw new IllegalStateException(
-                    "Virtual device already have a virtual display with ID " + displayId);
+        synchronized (mVirtualDeviceLock) {
+            if (mVirtualDisplayIds.contains(displayId)) {
+                throw new IllegalStateException(
+                        "Virtual device already have a virtual display with ID " + displayId);
+            }
+            mVirtualDisplayIds.add(displayId);
+
+            // Since we're being called in the middle of the display being created, we post a
+            // task to grab the wakelock instead of doing it synchronously here, to avoid
+            // reentrancy  problems.
+            mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId));
+
+            LocalServices.getService(
+                    InputManagerInternal.class).setDisplayEligibilityForPointerCapture(displayId,
+                    false);
+            final GenericWindowPolicyController dwpc =
+                    new GenericWindowPolicyController(FLAG_SECURE,
+                            SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+                            getAllowedUserHandles(),
+                            mParams.getAllowedActivities(),
+                            mParams.getBlockedActivities(),
+                            createListenerAdapter(displayId));
+            mWindowPolicyControllers.put(displayId, dwpc);
+            return dwpc;
         }
-        mVirtualDisplayIds.add(displayId);
-        LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
-                displayId, false);
-        final GenericWindowPolicyController dwpc =
-                new GenericWindowPolicyController(FLAG_SECURE,
-                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
-                        getAllowedUserHandles(),
-                        mParams.getAllowedActivities(),
-                        mParams.getBlockedActivities(),
-                        createListenerAdapter(displayId));
-        mWindowPolicyControllers.put(displayId, dwpc);
-        return dwpc;
+    }
+
+    void addWakeLockForDisplay(int displayId) {
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplayIds.contains(displayId)
+                    || mPerDisplayWakelocks.containsKey(displayId)) {
+                Slog.e(TAG, "Not creating wakelock for displayId " + displayId);
+                return;
+            }
+            PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+            PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+                    PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+                            | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+                    TAG + ":" + displayId, displayId);
+            wakeLock.acquire();
+            mPerDisplayWakelocks.put(displayId, wakeLock);
+        }
     }
 
     private ArraySet<UserHandle> getAllowedUserHandles() {
@@ -420,14 +460,22 @@
     }
 
     void onVirtualDisplayRemovedLocked(int displayId) {
-        if (!mVirtualDisplayIds.contains(displayId)) {
-            throw new IllegalStateException(
-                    "Virtual device doesn't have a virtual display with ID " + displayId);
+        synchronized (mVirtualDeviceLock) {
+            if (!mVirtualDisplayIds.contains(displayId)) {
+                throw new IllegalStateException(
+                        "Virtual device doesn't have a virtual display with ID " + displayId);
+            }
+            PowerManager.WakeLock wakeLock = mPerDisplayWakelocks.get(displayId);
+            if (wakeLock != null) {
+                wakeLock.release();
+                mPerDisplayWakelocks.remove(displayId);
+            }
+            mVirtualDisplayIds.remove(displayId);
+            LocalServices.getService(
+                    InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
+                    displayId, true);
+            mWindowPolicyControllers.remove(displayId);
         }
-        mVirtualDisplayIds.remove(displayId);
-        LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
-                displayId, true);
-        mWindowPolicyControllers.remove(displayId);
     }
 
     int getOwnerUid() {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 3951680..8551d88 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,12 +20,12 @@
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
 import static android.Manifest.permission.SHUTDOWN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_ALLOWLIST;
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
 import static android.net.INetd.FIREWALL_CHAIN_NONE;
-import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
-import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_DENYLIST;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.FIREWALL_RULE_DENY;
@@ -44,6 +44,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetdUnsolicitedEventListener;
 import android.net.INetworkManagementEventObserver;
@@ -1158,19 +1159,12 @@
             }
 
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
+            final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
             try {
                 if (allowlist) {
-                    if (enable) {
-                        mNetdService.bandwidthAddNiceApp(uid);
-                    } else {
-                        mNetdService.bandwidthRemoveNiceApp(uid);
-                    }
+                    cm.updateMeteredNetworkAllowList(uid, enable);
                 } else {
-                    if (enable) {
-                        mNetdService.bandwidthAddNaughtyApp(uid);
-                    } else {
-                        mNetdService.bandwidthRemoveNaughtyApp(uid);
-                    }
+                    cm.updateMeteredNetworkDenyList(uid, enable);
                 }
                 synchronized (mRulesLock) {
                     if (enable) {
@@ -1179,7 +1173,7 @@
                         quotaList.delete(uid);
                     }
                 }
-            } catch (RemoteException | ServiceSpecificException e) {
+            } catch (RuntimeException e) {
                 throw new IllegalStateException(e);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -1464,9 +1458,10 @@
                 throw new IllegalArgumentException("Bad child chain: " + chainName);
             }
 
+            final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
             try {
-                mNetdService.firewallEnableChildChain(chain, enable);
-            } catch (RemoteException | ServiceSpecificException e) {
+                cm.setFirewallChainEnabled(chain, enable);
+            } catch (RuntimeException e) {
                 throw new IllegalStateException(e);
             }
 
@@ -1538,25 +1533,10 @@
                     updateFirewallUidRuleLocked(chain, uid, FIREWALL_RULE_DEFAULT);
                 }
             }
+            final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
             try {
-                switch (chain) {
-                    case FIREWALL_CHAIN_DOZABLE:
-                        mNetdService.firewallReplaceUidChain("fw_dozable", true, uids);
-                        break;
-                    case FIREWALL_CHAIN_STANDBY:
-                        mNetdService.firewallReplaceUidChain("fw_standby", false, uids);
-                        break;
-                    case FIREWALL_CHAIN_POWERSAVE:
-                        mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
-                        break;
-                    case FIREWALL_CHAIN_RESTRICTED:
-                        mNetdService.firewallReplaceUidChain("fw_restricted", true, uids);
-                        break;
-                    case FIREWALL_CHAIN_NONE:
-                    default:
-                        Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
-                }
-            } catch (RemoteException e) {
+                cm.replaceFirewallChain(chain, uids);
+            } catch (RuntimeException e) {
                 Slog.w(TAG, "Error flushing firewall chain " + chain, e);
             }
         }
@@ -1572,10 +1552,10 @@
 
     private void setFirewallUidRuleLocked(int chain, int uid, int rule) {
         if (updateFirewallUidRuleLocked(chain, uid, rule)) {
-            final int ruleType = getFirewallRuleType(chain, rule);
+            final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
             try {
-                mNetdService.firewallSetUidRule(chain, uid, ruleType);
-            } catch (RemoteException | ServiceSpecificException e) {
+                cm.updateFirewallRule(chain, uid, isFirewallRuleAllow(chain, rule));
+            } catch (RuntimeException e) {
                 throw new IllegalStateException(e);
             }
         }
@@ -1645,12 +1625,12 @@
         }
     }
 
-    private int getFirewallRuleType(int chain, int rule) {
+    // There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY.
+    private boolean isFirewallRuleAllow(int chain, int rule) {
         if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
-            return getFirewallType(chain) == FIREWALL_ALLOWLIST
-                    ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
+            return getFirewallType(chain) == FIREWALL_DENYLIST;
         }
-        return rule;
+        return rule == INetd.FIREWALL_RULE_ALLOW;
     }
 
     private void enforceSystemUid() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c062365..b6a0ec4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -98,6 +98,7 @@
 import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.window.SplashScreen;
 
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.util.HexDump;
@@ -178,6 +179,7 @@
     private boolean mIsLockTask;
     private boolean mAsync;
     private BroadcastOptions mBroadcastOptions;
+    private boolean mShowSplashScreen;
 
     final boolean mDumping;
 
@@ -428,6 +430,8 @@
                     mBroadcastOptions.setBackgroundActivityStartsAllowed(true);
                 } else if (opt.equals("--async")) {
                     mAsync = true;
+                } else if (opt.equals("--splashscreen-show-icon")) {
+                    mShowSplashScreen = true;
                 } else {
                     return false;
                 }
@@ -566,6 +570,12 @@
                 }
                 options.setLockTaskEnabled(true);
             }
+            if (mShowSplashScreen) {
+                if (options == null) {
+                    options = ActivityOptions.makeBasic();
+                }
+                options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+            }
             if (mWaitOption) {
                 result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
                         mimeType, null, null, 0, mStartFlags, profilerInfo,
@@ -3301,6 +3311,7 @@
             pw.println("      --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into.");
             pw.println("      --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as.");
             pw.println("      --display <DISPLAY_ID>: The display to launch the activity into.");
+            pw.println("      --splashscreen-icon: Show the splash screen icon on launch.");
             pw.println("  start-service [--user <USER_ID> | current] <INTENT>");
             pw.println("      Start a Service.  Options are:");
             pw.println("      --user <USER_ID> | current: Specify which user to run as; if not");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e36ea20..bb939b7 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -67,6 +67,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.io.FileDescriptor;
@@ -1832,7 +1833,7 @@
 
     private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
             ComponentName component) {
-        if (info.activityInfo.attributionTags == null) {
+        if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {
             return noteOpForManifestReceiverInner(appOp, r, info, component, null);
         } else {
             // Attribution tags provided, noteOp each tag
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 3c5b872..df792ee2 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1046,6 +1046,16 @@
             userId = UserHandle.getCallingUserId();
         }
 
+        if (isAuthorityRedirectedForCloneProfile(authority)) {
+            UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+            UserInfo userInfo = umInternal.getUserInfo(userId);
+
+            if (userInfo != null && userInfo.isCloneProfile()) {
+                userId = umInternal.getProfileParentId(userId);
+                checkUser = false;
+            }
+        }
+
         ProviderInfo cpi = null;
         try {
             cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
@@ -1055,17 +1065,6 @@
                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                     userId);
-            if (cpi == null && isAuthorityRedirectedForCloneProfile(authority)) {
-                // This might be a provider that's running only in the system user that's
-                // also serving clone profiles
-                cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
-                        ActivityManagerService.STOCK_PM_FLAGS
-                                | PackageManager.GET_URI_PERMISSION_PATTERNS
-                                | PackageManager.MATCH_DISABLED_COMPONENTS
-                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        UserHandle.USER_SYSTEM);
-            }
         } catch (RemoteException ignored) {
         }
         if (cpi == null) {
@@ -1073,16 +1072,6 @@
                     + "; expected to find a valid ContentProvider for this authority";
         }
 
-        if (isAuthorityRedirectedForCloneProfile(authority)) {
-            UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
-            UserInfo userInfo = umInternal.getUserInfo(userId);
-
-            if (userInfo != null && userInfo.isCloneProfile()) {
-                userId = umInternal.getProfileParentId(userId);
-                checkUser = false;
-            }
-        }
-
         final int callingPid = Binder.getCallingPid();
         ProcessRecord r;
         final String appName;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 565e295..65be5f0 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -59,6 +59,7 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 
@@ -1794,4 +1795,10 @@
         }
         return null;
     }
+
+    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+        synchronized (mDeviceStateLock) {
+            return mDeviceInventory.getDeviceSensorUuid(device);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 2dd6bf5..f5a529b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -48,7 +48,9 @@
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Class to manage the inventory of all connected devices.
@@ -185,12 +187,20 @@
         final @NonNull String mDeviceName;
         final @NonNull String mDeviceAddress;
         int mDeviceCodecFormat;
+        final UUID mSensorUuid;
 
-        DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) {
+        DeviceInfo(int deviceType, String deviceName, String deviceAddress,
+                   int deviceCodecFormat, UUID sensorUuid) {
             mDeviceType = deviceType;
             mDeviceName = deviceName == null ? "" : deviceName;
             mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
             mDeviceCodecFormat = deviceCodecFormat;
+            mSensorUuid = sensorUuid;
+        }
+
+        DeviceInfo(int deviceType, String deviceName, String deviceAddress,
+                   int deviceCodecFormat) {
+            this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
         }
 
         @Override
@@ -199,7 +209,8 @@
                     + " (" + AudioSystem.getDeviceName(mDeviceType)
                     + ") name:" + mDeviceName
                     + " addr:" + mDeviceAddress
-                    + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]";
+                    + " codec: " + Integer.toHexString(mDeviceCodecFormat)
+                    + " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
         }
 
         @NonNull String getKey() {
@@ -980,8 +991,13 @@
         // Reset A2DP suspend state each time a new sink is connected
         mAudioSystem.setParameters("A2dpSuspended=false");
 
+        // The convention for head tracking sensors associated with A2DP devices is to
+        // use a UUID derived from the MAC address as follows:
+        //   time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
+        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
+                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
-                address, a2dpCodec);
+                address, a2dpCodec, sensorUuid);
         final String diKey = di.getKey();
         mConnectedDevices.put(diKey, di);
         // on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1456,6 +1472,17 @@
         mDevRoleCapturePresetDispatchers.finishBroadcast();
     }
 
+    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+        final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
+                device.getAddress());
+        synchronized (mDevicesLock) {
+            DeviceInfo di = mConnectedDevices.get(key);
+            if (di == null) {
+                return null;
+            }
+            return di.mSensorUuid;
+        }
+    }
     //----------------------------------------------------------
     // For tests only
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0ea936e..a853ac5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -190,6 +190,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -11017,6 +11018,10 @@
         return delayMillis;
     }
 
+    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+        return mDeviceBroker.getDeviceSensorUuid(device);
+    }
+
     //======================
     // misc
     //======================
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index e6789d5..106cbba 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -43,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.UUID;
 
 /**
  * A helper class to manage Spatializer related functionality
@@ -168,6 +169,7 @@
             return;
         }
         mState = STATE_DISABLED_UNAVAILABLE;
+        mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
         // note at this point mSpat is still not instantiated
     }
 
@@ -204,6 +206,11 @@
         logd("onRoutingUpdated: can spatialize media 5.1:" + able
                 + " on device:" + ROUTING_DEVICES[0]);
         setDispatchAvailableState(able);
+
+        if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED
+                && mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
+            postInitSensors();
+        }
     }
 
     //------------------------------------------------------
@@ -909,12 +916,21 @@
                 }
             }
             // initialize sensor handles
-            // TODO-HT update to non-private sensor once head tracker sensor is defined
-            for (Sensor sensor : mSensorManager.getDynamicSensorList(
-                    Sensor.TYPE_DEVICE_PRIVATE_BASE)) {
-                if (sensor.getStringType().equals(HEADTRACKER_SENSOR)) {
-                    headHandle = sensor.getHandle();
-                    break;
+            UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
+            List<Sensor> sensors = new ArrayList<Sensor>(0);
+            sensors.addAll(mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER));
+            sensors.addAll(mSensorManager.getDynamicSensorList(Sensor.TYPE_DEVICE_PRIVATE_BASE));
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == Sensor.TYPE_HEAD_TRACKER
+                        || sensor.getStringType().equals(HEADTRACKER_SENSOR)) {
+                    UUID uuid = sensor.getUuid();
+                    if (uuid.equals(routingDeviceUuid)) {
+                        headHandle = sensor.getHandle();
+                        break;
+                    }
+                    if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
+                        headHandle = sensor.getHandle();
+                    }
                 }
             }
             Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
diff --git a/services/core/java/com/android/server/audio/UuidUtils.java b/services/core/java/com/android/server/audio/UuidUtils.java
new file mode 100644
index 0000000..2003619
--- /dev/null
+++ b/services/core/java/com/android/server/audio/UuidUtils.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.audio;
+
+import android.media.AudioDeviceAttributes;
+import android.media.AudioSystem;
+import android.util.Slog;
+
+import java.util.UUID;
+
+/**
+ *  UuidUtils class implements helper functions to handle unique identifiers
+ *  used to associate head tracking sensors to audio devices.
+ */
+class UuidUtils {
+    private static final String TAG = "AudioService.UuidUtils";
+
+    private static final long LSB_PREFIX_MASK = 0xFFFF000000000000L;
+    private static final long LSB_SUFFIX_MASK = 0x0000FFFFFFFFFFFFL;
+    // The sensor UUID for Bluetooth devices is defined as follows:
+    // - 8 most significant bytes: All 0s
+    // - 8 most significant bytes: Ascii B, Ascii T, Device MAC address on 6 bytes
+    private static final long LSB_PREFIX_BT = 0x4254000000000000L;
+
+    /**
+     * Special UUID for a head tracking sensor not associated with an audio device.
+     */
+    public static final UUID STANDALONE_UUID = new UUID(0, 0);
+
+    /**
+     *  Generate a headtracking UUID from AudioDeviceAttributes
+     */
+    public static UUID uuidFromAudioDeviceAttributes(AudioDeviceAttributes device) {
+        switch (device.getInternalType()) {
+            case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP:
+                String address = device.getAddress().replace(":", "");
+                if (address.length() != 12) {
+                    return null;
+                }
+                address = "0x" + address;
+                long lsb = LSB_PREFIX_BT;
+                try {
+                    lsb |= Long.decode(address).longValue();
+                } catch (NumberFormatException e) {
+                    return null;
+                }
+                if (AudioService.DEBUG_DEVICES) {
+                    Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
+                }
+                return new UUID(0, lsb);
+            default:
+                // Handle other device types here
+                return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 3df2422..f3969b1 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.R;
 import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.config.BrightnessThresholds;
 import com.android.server.display.config.Density;
 import com.android.server.display.config.DisplayConfiguration;
 import com.android.server.display.config.DisplayQuirks;
@@ -42,6 +43,7 @@
 import com.android.server.display.config.RefreshRateRange;
 import com.android.server.display.config.SensorDetails;
 import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.config.Thresholds;
 import com.android.server.display.config.XmlParser;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -130,6 +132,10 @@
     private float mBrightnessRampSlowIncrease = Float.NaN;
     private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
     private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
+    private float mScreenBrighteningMinThreshold = 0.0f;     // Retain behaviour as though there is
+    private float mScreenDarkeningMinThreshold = 0.0f;       // no minimum threshold for change in
+    private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
+    private float mAmbientLuxDarkeningMinThreshold = 0.0f;   // brightness.
     private Spline mBrightnessToBacklightSpline;
     private Spline mBacklightToBrightnessSpline;
     private Spline mBacklightToNitsSpline;
@@ -364,6 +370,22 @@
         return mAmbientHorizonShort;
     }
 
+    public float getScreenBrighteningMinThreshold() {
+        return mScreenBrighteningMinThreshold;
+    }
+
+    public float getScreenDarkeningMinThreshold() {
+        return mScreenDarkeningMinThreshold;
+    }
+
+    public float getAmbientLuxBrighteningMinThreshold() {
+        return mAmbientLuxBrighteningMinThreshold;
+    }
+
+    public float getAmbientLuxDarkeningMinThreshold() {
+        return mAmbientLuxDarkeningMinThreshold;
+    }
+
     SensorData getAmbientLightSensor() {
         return mAmbientLightSensor;
     }
@@ -425,6 +447,10 @@
                 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
                 + ", mAmbientHorizonLong=" + mAmbientHorizonLong
                 + ", mAmbientHorizonShort=" + mAmbientHorizonShort
+                + ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
+                + ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
+                + ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
+                + ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
                 + ", mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mProximitySensor=" + mProximitySensor
                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -482,6 +508,7 @@
                 loadAmbientLightSensorFromDdc(config);
                 loadProxSensorFromDdc(config);
                 loadAmbientHorizonFromDdc(config);
+                loadBrightnessChangeThresholds(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -865,6 +892,45 @@
         }
     }
 
+    private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
+        Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
+        Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
+
+        if (displayBrightnessThresholds != null) {
+            BrightnessThresholds brighteningScreen =
+                    displayBrightnessThresholds.getBrighteningThresholds();
+            BrightnessThresholds darkeningScreen =
+                    displayBrightnessThresholds.getDarkeningThresholds();
+
+            final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
+            final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
+
+            if (screenBrighteningThreshold != null) {
+                mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
+            }
+            if (screenDarkeningThreshold != null) {
+                mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
+            }
+        }
+
+        if (ambientBrightnessThresholds != null) {
+            BrightnessThresholds brighteningAmbientLux =
+                    ambientBrightnessThresholds.getBrighteningThresholds();
+            BrightnessThresholds darkeningAmbientLux =
+                    ambientBrightnessThresholds.getDarkeningThresholds();
+
+            final BigDecimal ambientBrighteningThreshold = brighteningAmbientLux.getMinimum();
+            final BigDecimal ambientDarkeningThreshold =  darkeningAmbientLux.getMinimum();
+
+            if (ambientBrighteningThreshold != null) {
+                mAmbientLuxBrighteningMinThreshold = ambientBrighteningThreshold.floatValue();
+            }
+            if (ambientDarkeningThreshold != null) {
+                mAmbientLuxDarkeningMinThreshold = ambientDarkeningThreshold.floatValue();
+            }
+        }
+    }
+
     private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
         if (value == null) {
             return PowerManager.THERMAL_STATUS_NONE;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c6d3829..31c496ed 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -909,9 +909,14 @@
                     com.android.internal.R.array.config_ambientDarkeningThresholds);
             int[] ambientThresholdLevels = resources.getIntArray(
                     com.android.internal.R.array.config_ambientThresholdLevels);
+            float ambientDarkeningMinThreshold =
+                    mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold();
+            float ambientBrighteningMinThreshold =
+                    mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold();
             HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
                     ambientBrighteningThresholds, ambientDarkeningThresholds,
-                    ambientThresholdLevels);
+                    ambientThresholdLevels, ambientDarkeningMinThreshold,
+                    ambientBrighteningMinThreshold);
 
             int[] screenBrighteningThresholds = resources.getIntArray(
                     com.android.internal.R.array.config_screenBrighteningThresholds);
@@ -919,8 +924,13 @@
                     com.android.internal.R.array.config_screenDarkeningThresholds);
             int[] screenThresholdLevels = resources.getIntArray(
                     com.android.internal.R.array.config_screenThresholdLevels);
+            float screenDarkeningMinThreshold =
+                    mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
+            float screenBrighteningMinThreshold =
+                    mDisplayDeviceConfig.getScreenBrighteningMinThreshold();
             HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
-                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels);
+                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+                    screenDarkeningMinThreshold, screenBrighteningMinThreshold);
 
             long brighteningLightDebounce = resources.getInteger(
                     com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 2b56569..7a932ce 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -30,17 +30,13 @@
 public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
-    // Default hysteresis constraints for brightening or darkening.
-    // The recent value must have changed by at least this fraction relative to the
-    // current value before a change will be considered.
-    private static final float DEFAULT_BRIGHTENING_HYSTERESIS = 0.10f;
-    private static final float DEFAULT_DARKENING_HYSTERESIS = 0.20f;
-
     private static final boolean DEBUG = false;
 
     private final float[] mBrighteningThresholds;
     private final float[] mDarkeningThresholds;
     private final float[] mThresholdLevels;
+    private final float mMinDarkening;
+    private final float mMinBrightening;
 
     /**
      * Creates a {@code HysteresisLevels} object with the given equal-length
@@ -48,9 +44,12 @@
      * @param brighteningThresholds an array of brightening hysteresis constraint constants.
      * @param darkeningThresholds an array of darkening hysteresis constraint constants.
      * @param thresholdLevels a monotonically increasing array of threshold levels.
+     * @param minBrighteningThreshold the minimum value for which the brightening value needs to
+     *                                return.
+     * @param minDarkeningThreshold the minimum value for which the darkening value needs to return.
     */
     HysteresisLevels(int[] brighteningThresholds, int[] darkeningThresholds,
-            int[] thresholdLevels) {
+            int[] thresholdLevels, float minDarkeningThreshold, float minBrighteningThreshold) {
         if (brighteningThresholds.length != darkeningThresholds.length
                 || darkeningThresholds.length != thresholdLevels.length + 1) {
             throw new IllegalArgumentException("Mismatch between hysteresis array lengths.");
@@ -58,6 +57,8 @@
         mBrighteningThresholds = setArrayFormat(brighteningThresholds, 1000.0f);
         mDarkeningThresholds = setArrayFormat(darkeningThresholds, 1000.0f);
         mThresholdLevels = setArrayFormat(thresholdLevels, 1.0f);
+        mMinDarkening = minDarkeningThreshold;
+        mMinBrightening = minBrighteningThreshold;
     }
 
     /**
@@ -65,11 +66,13 @@
      */
     public float getBrighteningThreshold(float value) {
         final float brightConstant = getReferenceLevel(value, mBrighteningThresholds);
-        final float brightThreshold = value * (1.0f + brightConstant);
+        float brightThreshold = value * (1.0f + brightConstant);
         if (DEBUG) {
             Slog.d(TAG, "bright hysteresis constant=" + brightConstant + ", threshold="
                     + brightThreshold + ", value=" + value);
         }
+
+        brightThreshold = Math.max(brightThreshold, value + mMinBrightening);
         return brightThreshold;
     }
 
@@ -78,12 +81,13 @@
      */
     public float getDarkeningThreshold(float value) {
         final float darkConstant = getReferenceLevel(value, mDarkeningThresholds);
-        final float darkThreshold = value * (1.0f - darkConstant);
+        float darkThreshold = value * (1.0f - darkConstant);
         if (DEBUG) {
             Slog.d(TAG, "dark hysteresis constant=: " + darkConstant + ", threshold="
                     + darkThreshold + ", value=" + value);
         }
-        return darkThreshold;
+        darkThreshold = Math.min(darkThreshold, value - mMinDarkening);
+        return Math.max(darkThreshold, 0.0f);
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodDialogWindowContext.java b/services/core/java/com/android/server/inputmethod/InputMethodDialogWindowContext.java
new file mode 100644
index 0000000..1779333
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/InputMethodDialogWindowContext.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Provides the window context for the IME switcher dialog.
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class InputMethodDialogWindowContext {
+    @Nullable
+    private Context mDialogWindowContext;
+
+    /**
+     * Returns the window context for IME switch dialogs to receive configuration changes.
+     *
+     * This method initializes the window context if it was not initialized, or moves the context to
+     * the targeted display if the current display of context is different from the display
+     * specified by {@code displayId}.
+     */
+    @NonNull
+    @VisibleForTesting(visibility = PACKAGE)
+    public Context get(int displayId) {
+        if (mDialogWindowContext == null || mDialogWindowContext.getDisplayId() != displayId) {
+            final Context systemUiContext = ActivityThread.currentActivityThread()
+                    .getSystemUiContext(displayId);
+            final Context windowContext = systemUiContext.createWindowContext(
+                    WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
+            mDialogWindowContext = new ContextThemeWrapper(
+                    windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
+        }
+        return mDialogWindowContext;
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0d41a37..8a6aa0d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -160,7 +160,6 @@
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.DumpUtils;
@@ -270,7 +269,6 @@
     final WindowManagerInternal mWindowManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
-    private final HandlerCaller mCaller;
     final boolean mHasFeature;
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
             new ArrayMap<>();
@@ -1526,8 +1524,8 @@
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
             // Called on ActivityManager thread.
-            mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
-                    /* arg1= */ user.getUserIdentifier(), /* arg2= */ 0));
+            mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER, user.getUserIdentifier(), 0)
+                    .sendToTarget();
         }
     }
 
@@ -1587,8 +1585,6 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy;
-        mCaller = new HandlerCaller(context, thread.getLooper(), this::handleMessage,
-                true /*asyncHandler*/);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -2199,6 +2195,24 @@
         }
     }
 
+    @NonNull
+    private Message obtainMessageOO(int what, Object arg1, Object arg2) {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg1;
+        args.arg2 = arg2;
+        return mHandler.obtainMessage(what, 0, 0, args);
+    }
+
+    @NonNull
+    private Message obtainMessageIIIO(int what, int argi1, int argi2, int argi3, Object arg1) {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg1;
+        args.argi1 = argi1;
+        args.argi2 = argi2;
+        args.argi3 = argi3;
+        return mHandler.obtainMessage(what, 0, 0, args);
+    }
+
     private void executeOrSendMessage(IInputMethodClient target, Message msg) {
          if (target.asBinder() instanceof Binder) {
              // This is supposed to be emulating the one-way semantics when the IME client is
@@ -2207,7 +2221,7 @@
              // We probably should create a simple wrapper of IInputMethodClient as the first step
              // to get rid of executeOrSendMessage() then should prohibit system_server to be the
              // IME client for long term.
-             mCaller.sendMessage(msg);
+             msg.sendToTarget();
          } else {
              handleMessage(msg);
              msg.recycle();
@@ -2229,7 +2243,7 @@
 
             scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */,
                     false /* reportToImeController */);
-            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
+            executeOrSendMessage(mCurClient.client, mHandler.obtainMessage(
                     MSG_UNBIND_CLIENT, getSequenceNumberLocked(), unbindClientReason,
                     mCurClient.client));
             mCurClient.sessionRequested = false;
@@ -2506,8 +2520,9 @@
 
     @AnyThread
     void scheduleNotifyImeUidToAudioService(int uid) {
-        mCaller.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
-        mCaller.obtainMessageI(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid).sendToTarget();
+        mHandler.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
+        mHandler.obtainMessage(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid, 0 /* unused */)
+                .sendToTarget();
     }
 
     @BinderThread
@@ -2532,7 +2547,7 @@
                         InputBindResult res = attachNewInputLocked(
                                 StartInputReason.SESSION_CREATED_BY_IME, true);
                         if (res.method != null) {
-                            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
+                            executeOrSendMessage(mCurClient.client, obtainMessageOO(
                                     MSG_BIND_CLIENT, mCurClient.client, res));
                         }
                         return;
@@ -3637,9 +3652,10 @@
 
             // Always call subtype picker, because subtype picker is a superset of input method
             // picker.
-            mHandler.sendMessage(mCaller.obtainMessageII(
-                    MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
-                    (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
+            final int displayId =
+                    (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY;
+            mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
+                    .sendToTarget();
         }
     }
 
@@ -3654,8 +3670,8 @@
         }
         // Always call subtype picker, because subtype picker is a superset of input method
         // picker.
-        mHandler.sendMessage(mCaller.obtainMessageII(
-                MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
+        mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
+                .sendToTarget();
     }
 
     /**
@@ -3912,7 +3928,7 @@
     @Override
     public void removeImeSurface() {
         mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+        mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
     }
 
     @Override
@@ -4380,8 +4396,8 @@
 
     private void scheduleSetActiveToClient(ClientState state, boolean active, boolean fullscreen,
             boolean reportToImeController) {
-        executeOrSendMessage(state.client, mCaller.obtainMessageIIIIO(MSG_SET_ACTIVE,
-                active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state));
+        executeOrSendMessage(state.client, obtainMessageIIIO(MSG_SET_ACTIVE,
+                active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, state));
     }
 
     @GuardedBy("ImfLock.class")
@@ -4944,14 +4960,13 @@
 
         @Override
         public void removeImeSurface() {
-            mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+            mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
         }
 
         @Override
         public void updateImeWindowStatus(boolean disableImeIcon) {
-            mService.mHandler.sendMessage(
-                    mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS,
-                            disableImeIcon ? 1 : 0, 0));
+            mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
+                    .sendToTarget();
         }
     }
 
@@ -5017,8 +5032,9 @@
             }
             if (mCurClient != null && mCurClient.client != null) {
                 mInFullscreenMode = fullscreen;
-                executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                        MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, mCurClient));
+                executeOrSendMessage(mCurClient.client, mHandler.obtainMessage(
+                        MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0 /* unused */,
+                        mCurClient));
             }
         }
     }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index fcb1be0..348bb2d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -16,22 +16,19 @@
 
 package com.android.server.inputmethod;
 
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
 import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
 
-import android.app.ActivityThread;
+import android.annotation.Nullable;
 import android.app.AlertDialog;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,7 +42,6 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.wm.WindowManagerInternal;
@@ -53,8 +49,7 @@
 import java.util.List;
 
 /** A controller to show/hide the input method menu */
-@VisibleForTesting(visibility = PACKAGE)
-public class InputMethodMenuController {
+final class InputMethodMenuController {
     private static final String TAG = InputMethodMenuController.class.getSimpleName();
 
     private final InputMethodManagerService mService;
@@ -64,17 +59,18 @@
     private final KeyguardManager mKeyguardManager;
     private final WindowManagerInternal mWindowManagerInternal;
 
-    private Context mSettingsContext;
     private AlertDialog.Builder mDialogBuilder;
     private AlertDialog mSwitchingDialog;
-    private IBinder mSwitchingDialogToken;
     private View mSwitchingDialogTitleView;
     private InputMethodInfo[] mIms;
     private int[] mSubtypeIds;
 
     private boolean mShowImeWithHardKeyboard;
 
-    @VisibleForTesting(visibility = PACKAGE)
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    private InputMethodDialogWindowContext mDialogWindowContext;
+
     public InputMethodMenuController(InputMethodManagerService service) {
         mService = service;
         mSettings = mService.mSettings;
@@ -132,8 +128,11 @@
                 }
             }
 
-            final Context settingsContext = getSettingsContext(displayId);
-            mDialogBuilder = new AlertDialog.Builder(settingsContext);
+            if (mDialogWindowContext == null) {
+                mDialogWindowContext = new InputMethodDialogWindowContext();
+            }
+            final Context dialogWindowContext = mDialogWindowContext.get(displayId);
+            mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
             mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
 
             final Context dialogContext = mDialogBuilder.getContext();
@@ -199,7 +198,7 @@
             // Use an alternate token for the dialog for that window manager can group the token
             // with other IME windows based on type vs. grouping based on whichever token happens
             // to get selected by the system later on.
-            attrs.token = mSwitchingDialogToken;
+            attrs.token = dialogWindowContext.getWindowContextToken();
             attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             attrs.setTitle("Select input method");
             w.setAttributes(attrs);
@@ -208,27 +207,6 @@
         }
     }
 
-    /**
-     * Returns the window context for IME switch dialogs to receive configuration changes.
-     *
-     * This method initializes the window context if it was not initialized. This method also moves
-     * the context to the targeted display if the current display of context is different than
-     * the display specified by {@code displayId}.
-     */
-    @VisibleForTesting
-    public Context getSettingsContext(int displayId) {
-        if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
-            final Context systemUiContext = ActivityThread.currentActivityThread()
-                    .getSystemUiContext(displayId);
-            final Context windowContext = systemUiContext.createWindowContext(
-                    WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
-            mSettingsContext = new ContextThemeWrapper(
-                    windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
-            mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
-        }
-        return mSettingsContext;
-    }
-
     private boolean isScreenLocked() {
         return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()
                 && mKeyguardManager.isKeyguardSecure();
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 6676987..d48ccd5 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -69,7 +69,7 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -598,7 +598,7 @@
                     0,
                     0,
                     null,
-                    PackageUserState.DEFAULT,
+                    PackageUserStateInternal.DEFAULT,
                     UserHandle.getCallingUserId(),
                     null);
         } catch (Exception e) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e555c13..c01851a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -24,7 +24,6 @@
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
-import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -130,7 +129,6 @@
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
-import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -1010,10 +1008,11 @@
             userFilter.addAction(ACTION_USER_REMOVED);
             mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
 
-            // listen for stats update events
-            final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
-            mContext.registerReceiver(
-                    mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
+            // listen for stats updated callbacks for interested network types.
+            mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_MOBILE).build(),
+                    0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback);
+            mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_WIFI).build(),
+                    0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback);
 
             // Listen for snooze from notifications
             mContext.registerReceiver(mSnoozeReceiver,
@@ -1214,19 +1213,16 @@
     };
 
     /**
-     * Receiver that watches for {@link NetworkStatsManager} updates, which we
-     * use to check against {@link NetworkPolicy#warningBytes}.
+     * Listener that watches for {@link NetworkStatsManager} updates, which
+     * NetworkPolicyManagerService uses to check against {@link NetworkPolicy#warningBytes}.
      */
-    private final NetworkStatsBroadcastReceiver mStatsReceiver =
-            new NetworkStatsBroadcastReceiver();
-    private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
-        private boolean mIsAnyIntentReceived = false;
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // on background handler thread, and verified
-            // READ_NETWORK_USAGE_HISTORY permission above.
+    private final StatsCallback mStatsCallback = new StatsCallback();
+    private class StatsCallback extends NetworkStatsManager.UsageCallback {
+        private boolean mIsAnyCallbackReceived = false;
 
-            mIsAnyIntentReceived = true;
+        @Override
+        public void onThresholdReached(int networkType, String subscriberId) {
+            mIsAnyCallbackReceived = true;
 
             synchronized (mNetworkPoliciesSecondLock) {
                 updateNetworkRulesNL();
@@ -1236,11 +1232,11 @@
         }
 
         /**
-         * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+         * Return whether any callback is received.
          * Used to determine if NetworkStatsService is ready.
          */
-        public boolean isAnyIntentReceived() {
-            return mIsAnyIntentReceived;
+        public boolean isAnyCallbackReceived() {
+            return mIsAnyCallbackReceived;
         }
     };
 
@@ -1453,7 +1449,7 @@
 
         // Skip if not ready. NetworkStatsService will block public API calls until it is
         // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
-        if (!mStatsReceiver.isAnyIntentReceived()) return null;
+        if (!mStatsCallback.isAnyCallbackReceived()) return null;
 
         final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
         for (final NetworkStats.Bucket entry : stats) {
@@ -5450,7 +5446,7 @@
     private long getTotalBytes(NetworkTemplate template, long start, long end) {
         // Skip if not ready. NetworkStatsService will block public API calls until it is
         // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
-        if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+        if (!mStatsCallback.isAnyCallbackReceived()) return 0;
         return mDeps.getNetworkTotalBytes(template, start, end);
     }
 
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index cd4244b..ba003d2 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -36,14 +36,6 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -62,7 +54,15 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.component.ParsedService;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.WatchableImpl;
@@ -380,14 +380,13 @@
                     continue;
                 }
                 // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
-                if (metaDataKey != null
-                        && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
+                if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
                     continue;
                 }
                 if (appInfoGenerator == null) {
                     appInfoGenerator = new CachedApplicationInfoGenerator();
                 }
-                final PackageUserState state = ps.getUserStateOrDefault(userId);
+                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
                 final ApplicationInfo appInfo =
                         appInfoGenerator.generate(pkg, flags, state, userId, ps);
                 if (appInfo == null) {
@@ -424,7 +423,7 @@
             if (pkg == null) {
                 return null;
             }
-            final PackageUserState state = ps.getUserStateOrDefault(userId);
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
             ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
                     pkg, flags, state, userId, ps);
             if (appInfo == null) {
@@ -461,7 +460,7 @@
                 if (appInfoGenerator == null) {
                     appInfoGenerator = new CachedApplicationInfoGenerator();
                 }
-                final PackageUserState state = ps.getUserStateOrDefault(userId);
+                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
                 final ApplicationInfo appInfo =
                         appInfoGenerator.generate(pkg, 0, state, userId, ps);
                 if (appInfo == null) {
@@ -1537,7 +1536,7 @@
                 }
                 return null;
             }
-            final PackageUserState userState = ps.getUserStateOrDefault(userId);
+            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
             ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
                     userState, userId, ps);
             if (ai == null) {
@@ -1854,7 +1853,7 @@
             if (ps == null) {
                 return null;
             }
-            final PackageUserState userState = ps.getUserStateOrDefault(userId);
+            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
             final boolean matchVisibleToInstantApp = (mFlags
                     & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
             final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
@@ -2099,7 +2098,7 @@
             if (ps == null) {
                 return null;
             }
-            final PackageUserState userState = ps.getUserStateOrDefault(userId);
+            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
             ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
                     userState, userId, ps);
             if (si == null) {
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 69c475a..cca1b97 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -823,7 +823,7 @@
         }
         if (resolveComponentName().equals(component)) {
             return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
-                    flags, PackageUserState.DEFAULT, userId);
+                    flags, PackageUserStateInternal.DEFAULT, userId);
         }
         return null;
     }
@@ -1547,7 +1547,7 @@
             flags |= MATCH_ANY_USER;
         }
 
-        final PackageUserState state = ps.getUserStateOrDefault(userId);
+        final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
         AndroidPackage p = ps.getPkg();
         if (p != null) {
             // Compute GIDs only if requested
@@ -3548,7 +3548,7 @@
             if (shouldFilterApplication(ps, callingUid, userId)) {
                 return false;
             }
-            final PackageUserState state = ps.getUserStateOrDefault(userId);
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
             if (state != null) {
                 return PackageUserStateUtils.isAvailable(state, 0);
             }
@@ -4011,7 +4011,7 @@
                     ps, callingUid, component, TYPE_PROVIDER, userId)) {
                 return null;
             }
-            PackageUserState state = ps.getUserStateOrDefault(userId);
+            PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
             final ApplicationInfo appInfo =
                     PackageInfoUtils.generateApplicationInfo(ps.getPkg(), flags, state, userId, ps);
             if (appInfo == null) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index a3f1435..a3134a0 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -199,30 +199,6 @@
                     .addDataType("video/*")
                     .build();
 
-    // TODO(b/199068419): Remove once GEM enables the intent for Googlers
-    /** Pick images can be forwarded to work profile. */
-    private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES_TO_PROFILE =
-            new DefaultCrossProfileIntentFilter.Builder(
-                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
-                    /* flags= */ 0,
-                    /* letsPersonalDataIntoProfile= */ true)
-                    .addAction(MediaStore.ACTION_PICK_IMAGES)
-                    .addCategory(Intent.CATEGORY_DEFAULT)
-                    .build();
-    // TODO(b/199068419): Remove once GEM enables the intent for Googlers
-    /** Pick images can be forwarded to work profile. */
-    private static final DefaultCrossProfileIntentFilter
-            ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE =
-            new DefaultCrossProfileIntentFilter.Builder(
-                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
-                    /* flags= */ 0,
-                    /* letsPersonalDataIntoProfile= */ true)
-                    .addAction(MediaStore.ACTION_PICK_IMAGES)
-                    .addCategory(Intent.CATEGORY_DEFAULT)
-                    .addDataType("image/*")
-                    .addDataType("video/*")
-                    .build();
-
     /** Open document intent can be forwarded to parent user. */
     private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
             new DefaultCrossProfileIntentFilter.Builder(
@@ -336,8 +312,6 @@
                 ACTION_PICK_DATA,
                 ACTION_PICK_IMAGES,
                 ACTION_PICK_IMAGES_WITH_DATA_TYPES,
-                ACTION_PICK_IMAGES_TO_PROFILE,
-                ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE,
                 OPEN_DOCUMENT,
                 GET_CONTENT,
                 USB_DEVICE_ATTACHED,
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 9efe81a..06405ae 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -30,8 +30,10 @@
 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
 
 import android.annotation.Nullable;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -91,6 +93,29 @@
         mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
     }
 
+    private List<File> getFrameworkResApkSplitFiles() {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanFrameworkResApkSplits");
+        try {
+            final List<File> splits = new ArrayList<>();
+            final List<ApexManager.ActiveApexInfo> activeApexInfos =
+                    mPm.mApexManager.getActiveApexInfos();
+            for (int i = 0; i < activeApexInfos.size(); i++) {
+                ApexManager.ActiveApexInfo apexInfo = activeApexInfos.get(i);
+                File splitsFolder = new File(apexInfo.apexDirectory, "etc/splits");
+                if (splitsFolder.isDirectory()) {
+                    for (File file : splitsFolder.listFiles()) {
+                        if (ApkLiteParseUtils.isApkFile(file)) {
+                            splits.add(file);
+                        }
+                    }
+                }
+            }
+            return splits;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
     private List<ScanPartition> getSystemScanPartitions() {
         final List<ScanPartition> scanPartitions = new ArrayList<>();
         scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
@@ -184,7 +209,8 @@
         if (!mPm.isOnlyCoreApps()) {
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                     SystemClock.uptimeMillis());
-            scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+            scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
+                    mScanFlags | SCAN_REQUIRE_KNOWN, 0,
                     packageParser, executorService);
 
         }
@@ -247,12 +273,14 @@
             if (partition.getOverlayFolder() == null) {
                 continue;
             }
-            scanDirTracedLI(partition.getOverlayFolder(), mSystemParseFlags,
-                    mSystemScanFlags | partition.scanFlag, 0,
+            scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
+                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
                     packageParser, executorService);
         }
 
-        scanDirTracedLI(frameworkDir, mSystemParseFlags,
+        List<File> frameworkSplits = getFrameworkResApkSplitFiles();
+        scanDirTracedLI(frameworkDir, frameworkSplits,
+                mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
                 mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
                 packageParser, executorService);
         if (!mPm.mPackages.containsKey("android")) {
@@ -263,12 +291,13 @@
         for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
             final ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.getPrivAppFolder() != null) {
-                scanDirTracedLI(partition.getPrivAppFolder(), mSystemParseFlags,
+                scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
+                        mSystemParseFlags,
                         mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
                         packageParser, executorService);
             }
-            scanDirTracedLI(partition.getAppFolder(), mSystemParseFlags,
-                    mSystemScanFlags | partition.scanFlag, 0,
+            scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
+                    mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
                     packageParser, executorService);
         }
     }
@@ -285,12 +314,13 @@
     }
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
+    private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
+            final int parseFlags, int scanFlags,
             long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
         try {
-            mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags, scanFlags,
-                    currentTime, packageParser, executorService);
+            mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
+                    scanFlags, currentTime, packageParser, executorService);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 80699ac..d98626f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1806,10 +1806,8 @@
                 final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
                 if (ps != null && ps.isPrivileged()) {
                     fsverityCandidates.put(pkg.getBaseApkPath(), null);
-                    if (pkg.getSplitCodePaths() != null) {
-                        for (String splitPath : pkg.getSplitCodePaths()) {
-                            fsverityCandidates.put(splitPath, null);
-                        }
+                    for (String splitPath : pkg.getSplitCodePaths()) {
+                        fsverityCandidates.put(splitPath, null);
                     }
                 }
             }
@@ -1825,15 +1823,13 @@
                 fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
             }
 
-            if (pkg.getSplitCodePaths() != null) {
-                for (String path : pkg.getSplitCodePaths()) {
-                    fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+            for (String path : pkg.getSplitCodePaths()) {
+                fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
 
-                    final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
-                    if (new File(splitDmPath).exists()) {
-                        fsverityCandidates.put(splitDmPath,
-                                VerityUtils.getFsveritySignatureFilePath(splitDmPath));
-                    }
+                final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+                if (new File(splitDmPath).exists()) {
+                    fsverityCandidates.put(splitDmPath,
+                            VerityUtils.getFsveritySignatureFilePath(splitDmPath));
                 }
             }
         }
@@ -1989,9 +1985,7 @@
                             oldCodePaths = new ArraySet<>();
                         }
                         Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
-                        if (oldPackage.getSplitCodePaths() != null) {
-                            Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
-                        }
+                        Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
                         ps1.setOldCodePaths(oldCodePaths);
                     } else {
                         ps1.setOldCodePaths(null);
@@ -3412,8 +3406,9 @@
     }
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public void installPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
-            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+    public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
+            int scanFlags, long currentTime, PackageParser2 packageParser,
+            ExecutorService executorService) {
         final File[] files = scanDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             Log.d(TAG, "No files in app dir " + scanDir);
@@ -3425,7 +3420,7 @@
                     + " flags=0x" + Integer.toHexString(parseFlags));
         }
         ParallelPackageParser parallelPackageParser =
-                new ParallelPackageParser(packageParser, executorService);
+                new ParallelPackageParser(packageParser, executorService, frameworkSplits);
 
         // Submit files for parsing in parallel
         int fileCount = 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e00f4f5..e0d404a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -232,7 +232,7 @@
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.component.ParsedInstrumentation;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
@@ -8765,7 +8765,7 @@
             // The instance created in PackageManagerService is special cased to be non-user
             // specific, so initialize all the needed fields here.
             ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
 
             // Set up information for custom user intent resolution activity.
             mResolveActivity.applicationInfo = appInfo;
@@ -8797,7 +8797,7 @@
             // The instance stored in PackageManagerService is special cased to be non-user
             // specific, so initialize all the needed fields here.
             mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
 
             if (!mResolverReplaced) {
                 mResolveActivity.applicationInfo = mAndroidApplication;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index e03cf0a..1d2b829 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -228,11 +228,9 @@
         }
         final File baseFile = new File(pkg.getBaseApkPath());
         long maxModifiedTime = baseFile.lastModified();
-        if (pkg.getSplitCodePaths() != null) {
-            for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
-                final File splitFile = new File(pkg.getSplitCodePaths()[i]);
-                maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
-            }
+        for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
+            final File splitFile = new File(pkg.getSplitCodePaths()[i]);
+            maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
         }
         return maxModifiedTime;
     }
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9dbf57d..5fc840c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -295,14 +295,12 @@
             proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.getBaseRevisionCode());
             proto.end(splitToken);
 
-            if (pkg.getSplitNames() != null) {
-                for (int i = 0; i < pkg.getSplitNames().length; i++) {
-                    splitToken = proto.start(PackageProto.SPLITS);
-                    proto.write(PackageProto.SplitProto.NAME, pkg.getSplitNames()[i]);
-                    proto.write(PackageProto.SplitProto.REVISION_CODE,
-                            pkg.getSplitRevisionCodes()[i]);
-                    proto.end(splitToken);
-                }
+            for (int i = 0; i < pkg.getSplitNames().length; i++) {
+                splitToken = proto.start(PackageProto.SPLITS);
+                proto.write(PackageProto.SplitProto.NAME, pkg.getSplitNames()[i]);
+                proto.write(PackageProto.SplitProto.REVISION_CODE,
+                        pkg.getSplitRevisionCodes()[i]);
+                proto.end(splitToken);
             }
 
             long sourceToken = proto.start(PackageProto.INSTALL_SOURCE);
@@ -1263,8 +1261,8 @@
 
     @Nullable
     @Override
-    public Integer getSharedUserId() {
-        return sharedUser == null ? null : sharedUser.userId;
+    public int getSharedUserId() {
+        return sharedUser == null ? -1 : sharedUser.userId;
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 5625884..45030bf 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -27,6 +27,7 @@
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 
 import java.io.File;
+import java.util.List;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
@@ -54,9 +55,17 @@
 
     private final ExecutorService mExecutorService;
 
+    private final List<File> mFrameworkSplits;
+
     ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
+        this(packageParser, executorService, /* frameworkSplits= */ null);
+    }
+
+    ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService,
+            List<File> frameworkSplits) {
         mPackageParser = packageParser;
         mExecutorService = executorService;
+        mFrameworkSplits = frameworkSplits;
     }
 
     static class ParseResult {
@@ -125,6 +134,6 @@
     @VisibleForTesting
     protected ParsedPackage parsePackage(File scanFile, int parseFlags)
             throws PackageManagerException {
-        return mPackageParser.parsePackage(scanFile, parseFlags, true);
+        return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits);
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 45837717..f21bc93 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4245,7 +4245,7 @@
         final PackageSetting ps = mPackages.get(componentInfo.packageName);
         if (ps == null) return false;
 
-        final PackageUserState userState = ps.readUserState(userId);
+        final PackageUserStateInternal userState = ps.readUserState(userId);
         return PackageUserStateUtils.isMatch(userState, componentInfo, flags);
     }
 
@@ -4255,7 +4255,7 @@
         final PackageSetting ps = mPackages.get(component.getPackageName());
         if (ps == null) return false;
 
-        final PackageUserState userState = ps.readUserState(userId);
+        final PackageUserStateInternal userState = ps.readUserState(userId);
         return PackageUserStateUtils.isMatch(userState, pkg.isSystem(), pkg.isEnabled(), component,
                 flags);
     }
@@ -4497,13 +4497,11 @@
                 pw.print(checkinTag); pw.print("-"); pw.print("splt,");
                 pw.print("base,");
                 pw.println(pkg.getBaseRevisionCode());
-                if (pkg.getSplitNames() != null) {
-                    int[] splitRevisionCodes = pkg.getSplitRevisionCodes();
-                    for (int i = 0; i < pkg.getSplitNames().length; i++) {
-                        pw.print(checkinTag); pw.print("-"); pw.print("splt,");
-                        pw.print(pkg.getSplitNames()[i]); pw.print(",");
-                        pw.println(splitRevisionCodes[i]);
-                    }
+                int[] splitRevisionCodes = pkg.getSplitRevisionCodes();
+                for (int i = 0; i < pkg.getSplitNames().length; i++) {
+                    pw.print(checkinTag); pw.print("-"); pw.print("splt,");
+                    pw.print(pkg.getSplitNames()[i]); pw.print(",");
+                    pw.println(splitRevisionCodes[i]);
                 }
             }
             for (UserInfo user : users) {
@@ -5169,13 +5167,11 @@
             }
             String[] splitNames = pkg.getSplitNames();
             int[] splitRevisionCodes = pkg.getSplitRevisionCodes();
-            if (splitNames != null) {
-                for (int i = 0; i < splitNames.length; i++) {
-                    pw.print(", ");
-                    pw.print(splitNames[i]);
-                    if (splitRevisionCodes[i] != 0) {
-                        pw.print(":"); pw.print(splitRevisionCodes[i]);
-                    }
+            for (int i = 0; i < splitNames.length; i++) {
+                pw.print(", ");
+                pw.print(splitNames[i]);
+                if (splitRevisionCodes[i] != 0) {
+                    pw.print(":"); pw.print(splitRevisionCodes[i]);
                 }
             }
             pw.print("]");
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index fc88af9..beea86d 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -78,7 +78,7 @@
 
         String baseApkContextClassLoader = encodeClassLoader(
                 "", pkg.getClassLoaderName(), sharedLibrariesContext);
-        if (pkg.getSplitCodePaths() == null) {
+        if (ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
             // The application has no splits.
             return new String[] {baseApkContextClassLoader};
         }
diff --git a/services/core/java/com/android/server/pm/package-info.java b/services/core/java/com/android/server/pm/package-info.java
new file mode 100644
index 0000000..04b8e0a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * @hide
+ * TODO(b/146466118) remove this javadoc tag
+ */
+@android.annotation.Hide
+package com.android.server.pm;
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 0fa0dc3..2d0a3ef 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -59,7 +59,7 @@
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUnserialized;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 
 import libcore.util.EmptyArray;
 
@@ -85,8 +85,8 @@
     @Nullable
     public static PackageInfo generate(AndroidPackage pkg, int[] gids,
             @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
-            @Nullable PackageStateInternal pkgSetting) {
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserStateInternal state,
+            @UserIdInt int userId, @Nullable PackageStateInternal pkgSetting) {
         return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
                 grantedPermissions, state, userId, null, pkgSetting);
     }
@@ -98,7 +98,7 @@
     public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, int flags,
             @Nullable PackageStateInternal pkgSetting) {
         return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
-                PackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo, pkgSetting);
+                PackageUserStateInternal.DEFAULT, UserHandle.getCallingUserId(), apexInfo, pkgSetting);
     }
 
     /**
@@ -106,8 +106,9 @@
      */
     private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
             @PackageManager.PackageInfoFlagsBits long flags, long firstInstallTime,
-            long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId,
-            @Nullable ApexInfo apexInfo, @Nullable PackageStateInternal pkgSetting) {
+            long lastUpdateTime, Set<String> grantedPermissions, PackageUserStateInternal state,
+            @UserIdInt int userId, @Nullable ApexInfo apexInfo,
+            @Nullable PackageStateInternal pkgSetting) {
         ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
                 pkgSetting);
         if (applicationInfo == null) {
@@ -209,8 +210,9 @@
      */
     @Nullable
     public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
-            @PackageManager.ApplicationInfoFlagsBits long flags, @NonNull PackageUserState state,
-            int userId, @Nullable PackageStateInternal pkgSetting) {
+            @PackageManager.ApplicationInfoFlagsBits long flags,
+            @NonNull PackageUserStateInternal state, @UserIdInt int userId,
+            @Nullable PackageStateInternal pkgSetting) {
         if (pkg == null) {
             return null;
         }
@@ -255,7 +257,8 @@
      */
     @Nullable
     public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull PackageUserStateInternal state, @UserIdInt int userId,
             @Nullable PackageStateInternal pkgSetting) {
         return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
     }
@@ -265,9 +268,9 @@
      */
     @Nullable
     private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
-            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
-            @Nullable ApplicationInfo applicationInfo, int userId,
-            @Nullable PackageStateInternal pkgSetting) {
+            @PackageManager.ComponentInfoFlagsBits long flags,
+            @NonNull PackageUserStateInternal state, @Nullable ApplicationInfo applicationInfo,
+            @UserIdInt int userId, @Nullable PackageStateInternal pkgSetting) {
         if (a == null) return null;
         if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
             return null;
@@ -291,8 +294,8 @@
      */
     @Nullable
     public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state, int userId,
-            @Nullable PackageStateInternal pkgSetting) {
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
+            @UserIdInt int userId, @Nullable PackageStateInternal pkgSetting) {
         return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
     }
 
@@ -301,7 +304,7 @@
      */
     @Nullable
     private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
-            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
             @Nullable ApplicationInfo applicationInfo, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (s == null) return null;
@@ -326,7 +329,7 @@
      */
     @Nullable
     public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserState state,
+            @PackageManager.ComponentInfoFlagsBits long flags, PackageUserStateInternal state,
             @NonNull ApplicationInfo applicationInfo, int userId,
             @Nullable PackageStateInternal pkgSetting) {
         if (p == null) return null;
@@ -418,11 +421,11 @@
     }
 
     /**
-     * Returns true if the package is installed and not hidden, or if the caller
-     * explicitly wanted all uninstalled and hidden packages as well.
+     * Returns true if the package is installed and not hidden, or if the caller explicitly wanted
+     * all uninstalled and hidden packages as well.
      */
     public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
-            PackageStateInternal pkgSetting, PackageUserState state,
+            PackageStateInternal pkgSetting, PackageUserStateInternal state,
             @PackageManager.PackageInfoFlagsBits long flags) {
         // Returns false if the package is hidden system app until installed.
         if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
@@ -466,7 +469,9 @@
         return hasFlag ? flag : 0;
     }
 
-    /** @see ApplicationInfo#flags */
+    /**
+     * @see ApplicationInfo#flags
+     */
     public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageStateInternal pkgSetting) {
         // @formatter:off
         int pkgWithoutStateFlags = PackageInfoWithoutStateUtils.appInfoFlags(pkg)
@@ -628,7 +633,7 @@
          */
         @Nullable
         public ApplicationInfo generate(AndroidPackage pkg,
-                @PackageManager.ApplicationInfoFlagsBits long flags, PackageUserState state,
+                @PackageManager.ApplicationInfoFlagsBits long flags, PackageUserStateInternal state,
                 int userId, @Nullable PackageStateInternal pkgSetting) {
             ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
             if (appInfo != null) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 08e2f7d..b2e15e7 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -22,9 +22,6 @@
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
@@ -41,6 +38,9 @@
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import java.io.File;
 import java.util.List;
@@ -147,6 +147,15 @@
     @AnyThread
     public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
             throws PackageManagerException {
+        return parsePackage(packageFile, flags, useCaches, /* frameworkSplits= */ null);
+    }
+
+    /**
+     * TODO(b/135203078): Document new package parsing
+     */
+    @AnyThread
+    public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
+            List<File> frameworkSplits) throws PackageManagerException {
         if (useCaches && mCacher != null) {
             ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
             if (parsed != null) {
@@ -156,7 +165,8 @@
 
         long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
         ParseInput input = mSharedResult.get().reset();
-        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
+        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
+                frameworkSplits);
         if (result.isError()) {
             throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
                     result.getException());
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 9b3d6d6..8e41c9b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -73,11 +73,6 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
@@ -136,6 +131,10 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedPermissionUtils;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.SoftRestrictedPermissionPolicy;
 
@@ -3172,18 +3171,17 @@
                 }
             } else if (NOTIFICATION_PERMISSIONS.contains(newPerm)) {
                 //&& (origPs.getPermissionState(newPerm) == null) {
-                // TODO(b/205888750): add back line about origPs once propagated through droidfood
+                // TODO(b/205888750): add back line about origPs once all TODO sections below are
+                //  propagated through droidfood
                 Permission bp = mRegistry.getPermission(newPerm);
                 if (bp == null) {
                     throw new IllegalStateException("Unknown new permission " + newPerm);
                 }
-                // TODO(b/205888750): remove the line for REVOKE_WHEN_REQUESTED once propagated
-                //  through droidfood
                 if (!isUserSetOrPregrantedOrFixed(ps.getPermissionFlags(newPerm))) {
                     updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
-                    ps.updatePermissionFlags(bp, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
-                                    | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
-                            PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED);
+                    int setFlag = ps.isPermissionGranted(newPerm)
+                            ? 0 : FLAG_PERMISSION_REVIEW_REQUIRED;
+                    ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVIEW_REQUIRED, setFlag);
                     // TODO(b/205888750): remove if/else block once propagated through droidfood
                     if (ps.isPermissionGranted(newPerm)
                             && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
@@ -3192,6 +3190,10 @@
                             && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
                         ps.grantPermission(bp);
                     }
+                } else {
+                    // TODO(b/205888750): remove once propagated through droidfood
+                    ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+                            | FLAG_PERMISSION_REVIEW_REQUIRED, 0);
                 }
             }
         }
@@ -4779,9 +4781,10 @@
 
         // Handle REVIEW_REQUIRED
         if ((newFlags & priorityFixedMask) == 0) {
-            if (NOTIFICATION_PERMISSIONS.contains(srcState.getName())) {
+            if ((newFlags & (defaultGrantMask | userSettableMask)) == 0
+                    && NOTIFICATION_PERMISSIONS.contains(srcState.getName())) {
                 // For notification permissions, inherit from both states
-                // if no priority FIXED flags are set
+                // if no priority FIXED or DEFAULT_GRANT or USER_SET flags are set
                 newFlags |= (combinedFlags & FLAG_PERMISSION_REVIEW_REQUIRED);
             } else if ((newFlags & priorityMask) == 0) {
                 // Else inherit from destState if no priority flags are set
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java b/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
index 3fde41a..d3c8c7b 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageApi.java
@@ -16,21 +16,337 @@
 
 package com.android.server.pm.pkg;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.util.SparseArray;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.PkgAppInfo;
-import com.android.server.pm.parsing.pkg.PkgPackageInfo;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedAttribution;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
 
 /**
  * Explicit interface used for consumers like mainline who need a {@link SystemApi @SystemApi} form
- * of {@link AndroidPackage}.
- * <p>
- * There should be no methods in this class. All of them must come from other interfaces that group
- * the actual methods. This is done to ensure proper separation of the (legacy?) Info object APIs.
+ * of {@link AndroidPackage}. *
+ * @hide
  */
-// TODO(b/173807334): Expose API
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface AndroidPackageApi extends PkgPackageInfo, PkgAppInfo {
+public interface AndroidPackageApi {
 
+    boolean areAttributionsUserVisible();
+
+    @Nullable
+    String getAppComponentFactory();
+
+    int getAutoRevokePermissions();
+
+    @Nullable
+    String getBackupAgentName();
+
+    int getBanner();
+
+    @NonNull
+    String getBaseApkPath();
+
+    int getCategory();
+
+    @Nullable
+    String getClassLoaderName();
+
+    @Nullable
+    String getClassName();
+
+    int getCompatibleWidthLimitDp();
+
+    int getDataExtractionRules();
+
+    int getDescriptionRes();
+
+    int getFullBackupContent();
+
+    int getGwpAsanMode();
+
+    int getIconRes();
+
+    int getInstallLocation();
+
+    int getLabelRes();
+
+    int getLargestWidthLimitDp();
+
+    int getLogo();
+
+    @Nullable
+    String getManageSpaceActivityName();
+
+    float getMaxAspectRatio();
+
+    int getMemtagMode();
+
+    float getMinAspectRatio();
+
+    int getMinSdkVersion();
+
+    int getNativeHeapZeroInitialized();
+
+    int getNetworkSecurityConfigRes();
+
+    @Nullable
+    CharSequence getNonLocalizedLabel();
+
+    @NonNull
+    String getPath();
+
+    @Nullable
+    String getPermission();
+
+    @NonNull
+    String getProcessName();
+
+    int getRequiresSmallestWidthDp();
+
+    @SuppressLint("AutoBoxing")
+    @Nullable
+    Boolean getResizeableActivity();
+
+    int getRoundIconRes();
+
+    @NonNull
+    String[] getSplitClassLoaderNames();
+
+    @NonNull
+    String[] getSplitCodePaths();
+
+    @Nullable
+    SparseArray<int[]> getSplitDependencies();
+
+    int getTargetSdkVersion();
+
+    int getTargetSandboxVersion();
+
+    @Nullable
+    String getTaskAffinity();
+
+    int getTheme();
+
+    int getUiOptions();
+
+    @Nullable
+    String getVolumeUuid();
+
+    @Nullable
+    String getZygotePreloadName();
+
+    boolean hasRequestForegroundServiceExemption();
+
+    @SuppressLint("AutoBoxing")
+    @Nullable
+    Boolean hasRequestRawExternalStorageAccess();
+
+    boolean isAllowAudioPlaybackCapture();
+
+    boolean isAllowBackup();
+
+    boolean isAllowClearUserData();
+
+    boolean isAllowClearUserDataOnFailedRestore();
+
+    boolean isAllowNativeHeapPointerTagging();
+
+    boolean isAllowTaskReparenting();
+
+    boolean isAnyDensity();
+
+    boolean isBackupInForeground();
+
+    boolean isBaseHardwareAccelerated();
+
+    boolean isCantSaveState();
+
+    boolean isCrossProfile();
+
+    boolean isDebuggable();
+
+    boolean isDefaultToDeviceProtectedStorage();
+
+    boolean isDirectBootAware();
+
+    boolean isEnabled();
+
+    boolean isExternalStorage();
+
+    boolean isExtractNativeLibs();
+
+    boolean isFullBackupOnly();
+
+    boolean isHasCode();
+
+    boolean isHasDomainUrls();
+
+    boolean isHasFragileUserData();
+
+    boolean isIsolatedSplitLoading();
+
+    boolean isKillAfterRestore();
+
+    boolean isLargeHeap();
+
+    boolean isMultiArch();
+
+    boolean isOverlay();
+
+    boolean isPartiallyDirectBootAware();
+
+    boolean isPersistent();
+
+    boolean isProfileable();
+
+    boolean isProfileableByShell();
+
+    boolean isRequestLegacyExternalStorage();
+
+    boolean isResizeable();
+
+    boolean isResizeableActivityViaSdkVersion();
+
+    boolean isRestoreAnyVersion();
+
+    boolean isStaticSharedLibrary();
+
+    boolean isSdkLibrary();
+
+    boolean isSupportsExtraLargeScreens();
+
+    boolean isSupportsLargeScreens();
+
+    boolean isSupportsNormalScreens();
+
+    boolean isSupportsRtl();
+
+    boolean isSupportsSmallScreens();
+
+    boolean isTestOnly();
+
+    boolean isUseEmbeddedDex();
+
+    boolean isUsesCleartextTraffic();
+
+    boolean isUsesNonSdkApi();
+
+    boolean isVmSafeMode();
+
+    @NonNull
+    List<ParsedActivity> getActivities();
+
+    @NonNull
+    List<ParsedAttribution> getAttributions();
+
+    @NonNull
+    List<String> getAdoptPermissions();
+
+    int getBaseRevisionCode();
+
+    int getCompileSdkVersion();
+
+    @Nullable
+    String getCompileSdkVersionCodeName();
+
+    @NonNull
+    List<ConfigurationInfo> getConfigPreferences();
+
+    @NonNull
+    List<FeatureGroupInfo> getFeatureGroups();
+
+    @NonNull
+    List<ParsedInstrumentation> getInstrumentations();
+
+    long getLongVersionCode();
+
+    @NonNull
+    String getPackageName();
+
+    @NonNull
+    List<ParsedPermission> getPermissions();
+
+    @NonNull
+    List<ParsedProvider> getProviders();
+
+    @NonNull
+    List<ParsedActivity> getReceivers();
+
+    @NonNull
+    List<FeatureInfo> getRequestedFeatures();
+
+    @NonNull
+    List<String> getRequestedPermissions();
+
+    @Nullable
+    String getRequiredAccountType();
+
+    @Nullable
+    String getRestrictedAccountType();
+
+    @NonNull
+    List<ParsedService> getServices();
+
+    @Nullable
+    String getSharedUserId();
+
+    int getSharedUserLabel();
+
+    @NonNull
+    String[] getSplitNames();
+
+    @NonNull
+    int[] getSplitRevisionCodes();
+
+    @Nullable
+    String getVersionName();
+
+    boolean isRequiredForAllUsers();
+
+    @Nullable
+    String getNativeLibraryDir();
+
+    @Nullable
+    String getNativeLibraryRootDir();
+
+    @Nullable
+    String getSecondaryNativeLibraryDir();
+
+    int getUid();
+
+    boolean isFactoryTest();
+
+    boolean isNativeLibraryRootRequiresIsa();
+
+    boolean isOdm();
+
+    boolean isOem();
+
+    boolean isPrivileged();
+
+    boolean isProduct();
+
+    boolean isSignedWithPlatformKey();
+
+    boolean isSystem();
+
+    boolean isSystemExt();
+
+    boolean isVendor();
+
+    boolean isCoreApp();
+
+    boolean isStub();
 }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index f5ee8d9..9fa9644 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -54,7 +54,6 @@
  *
  * @hide
  */
-// TODO(b/173807334): Expose API
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface PackageState {
 
@@ -163,10 +162,9 @@
      * Retrieves the shared user ID. Note that the actual shared user data is not available here and
      * must be queried separately.
      *
-     * @return the shared user this package is a part of, or null if it's not part of a shared user.
+     * @return the shared user this package is a part of, or -1 if it's not part of a shared user.
      */
-    @Nullable
-    Integer getSharedUserId();
+    int getSharedUserId();
 
     @NonNull
     SigningInfo getSigningInfo();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index a5d399e..9395ca5 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -127,7 +127,7 @@
     @Nullable
     private final String mSecondaryCpuAbi;
     @Nullable
-    private final Integer mSharedUserId;
+    private final int mSharedUserId;
     @NonNull
     private final String[] mUsesSdkLibraries;
     @NonNull
@@ -615,7 +615,7 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable Integer getSharedUserId() {
+    public @Nullable int getSharedUserId() {
         return mSharedUserId;
     }
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 656c445..91e9b2f 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -58,7 +58,7 @@
             ComponentInfo componentInfo, long flags, int userId) {
         if (packageState == null) return false;
 
-        final PackageUserState userState = packageState.getUserStateOrDefault(userId);
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
         return PackageUserStateUtils.isMatch(userState, componentInfo, flags);
     }
 
@@ -72,7 +72,7 @@
         if (pkg == null) {
             return false;
         }
-        final PackageUserState userState = packageState.getUserStateOrDefault(userId);
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
         return PackageUserStateUtils.isMatch(userState, packageState.isSystem(),
                 pkg.isEnabled(), component, flags);
     }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index d47c5ec..bed1401 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -18,6 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
 import android.content.pm.overlay.OverlayPaths;
 import android.os.UserHandle;
@@ -30,15 +32,20 @@
  *
  * The parent of this class is {@link PackageState}, which handles non-user state, exposing this
  * interface for per-user state.
+ *
+ * @hide
  */
 // TODO(b/173807334): Expose API
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface PackageUserState {
 
+    /** @hide */
+    @NonNull
     PackageUserState DEFAULT = PackageUserStateInternal.DEFAULT;
 
     /**
      * Combination of {@link #getOverlayPaths()} and {@link #getSharedLibraryOverlayPaths()}
+     * @hide
      */
     @Nullable
     OverlayPaths getAllOverlayPaths();
@@ -84,9 +91,11 @@
     @Nullable
     String getLastDisableAppCaller();
 
+    /** @hide */
     @Nullable
     OverlayPaths getOverlayPaths();
 
+    /** @hide */
     @NonNull
     Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index bd8b3ab..bc521ce 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.pm.pkg.FrameworkPackageUserState;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -28,7 +29,7 @@
  * still read-only and should be used inside system server code when possible over the
  * implementation.
  */
-public interface PackageUserStateInternal extends PackageUserState {
+public interface PackageUserStateInternal extends PackageUserState, FrameworkPackageUserState {
 
     PackageUserStateInternal DEFAULT = new PackageUserStateDefault();
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index 6d978c4..c2b3cbc 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -21,11 +21,13 @@
 import android.content.pm.ActivityInfo;
 
 /** @hide **/
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedActivity extends ParsedMainComponent {
 
     /**
      * Generate activity object that forwards user to App Details page automatically.
      * This activity should be invisible to user and user should not know or see it.
+     * @hide
      */
     @NonNull
     static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index ff97c13..91c0b07 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -20,16 +20,16 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -37,11 +37,15 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
-/** @hide **/
+/**
+ * @hide
+ **/
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity {
+public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity,
+        Parcelable {
 
     private int theme;
     private int uiOptions;
@@ -112,8 +116,8 @@
     }
 
     /**
-     * Generate activity object that forwards user to App Details page automatically.
-     * This activity should be invisible to user and user should not know or see it.
+     * Generate activity object that forwards user to App Details page automatically. This activity
+     * should be invisible to user and user should not know or see it.
      */
     @NonNull
     static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
@@ -641,10 +645,10 @@
     }
 
     @DataClass.Generated(
-            time = 1630600615936L,
+            time = 1641431949361L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
-            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index db8815e..a6c22a18 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -18,8 +18,9 @@
 
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+
 import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,9 +28,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
 import android.content.pm.parsing.result.ParseResult;
@@ -49,6 +47,9 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -353,10 +354,10 @@
 
             final ParseResult result;
             if (parser.getName().equals("intent-filter")) {
-                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                ParseResult<ParsedIntentInfoImpl> intentResult = parseIntentFilter(pkg, activity,
                         !isReceiver, visibleToEphemeral, resources, parser, input);
                 if (intentResult.isSuccess()) {
-                    ParsedIntentInfo intentInfo = intentResult.getResult();
+                    ParsedIntentInfoImpl intentInfo = intentResult.getResult();
                     if (intentInfo != null) {
                         IntentFilter intentFilter = intentInfo.getIntentFilter();
                         activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
@@ -386,11 +387,11 @@
             } else if (parser.getName().equals("property")) {
                 result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
             } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
-                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+                ParseResult<ParsedIntentInfoImpl> intentResult = parseIntentFilter(pkg, activity,
                         true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
                         resources, parser, input);
                 if (intentResult.isSuccess()) {
-                    ParsedIntentInfo intent = intentResult.getResult();
+                    ParsedIntentInfoImpl intent = intentResult.getResult();
                     if (intent != null) {
                         pkg.addPreferredActivityFilter(activity.getClassName(), intent);
                     }
@@ -413,7 +414,7 @@
         }
 
         if (!isAlias && activity.getLaunchMode() != LAUNCH_SINGLE_INSTANCE_PER_TASK
-                && activity.getMetaData() != null && activity.getMetaData().containsKey(
+                && activity.getMetaData().containsKey(
                 ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
             final String launchMode = activity.getMetaData().getString(
                     ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
@@ -427,7 +428,7 @@
             // set to false.
             boolean canDisplayOnRemoteDevices = array.getBoolean(
                     R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
-            if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
+            if (!activity.getMetaData().getBoolean(
                     ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
                 canDisplayOnRemoteDevices = false;
             }
@@ -463,11 +464,11 @@
     }
 
     @NonNull
-    private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
+    private static ParseResult<ParsedIntentInfoImpl> parseIntentFilter(ParsingPackage pkg,
             ParsedActivityImpl activity, boolean allowImplicitEphemeralVisibility,
             boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
             ParseInput input) throws IOException, XmlPullParserException {
-        ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
+        ParseResult<ParsedIntentInfoImpl> result = ParsedMainComponentUtils.parseIntentFilter(activity,
                 pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/,
                 true /*allowAutoVerify*/, allowImplicitEphemeralVisibility,
                 true /*failOnNoActions*/, input);
@@ -475,7 +476,7 @@
             return input.error(result);
         }
 
-        ParsedIntentInfo intent = result.getResult();
+        ParsedIntentInfoImpl intent = result.getResult();
         if (intent != null) {
             final IntentFilter intentFilter = intent.getIntentFilter();
             if (intentFilter.isVisibleToInstantApp()) {
@@ -574,7 +575,7 @@
     private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
             ParsedActivity activity, ParseInput input) {
         // There isn't a metadata for us to fall back. Whatever is in layout is correct.
-        if (activity.getMetaData() == null || !activity.getMetaData().containsKey(
+        if (!activity.getMetaData().containsKey(
                 ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
             return input.success(activity.getWindowLayout());
         }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
index 7690818..586d2c4 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
@@ -18,10 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Parcelable;
 
 /** @hide */
-public interface ParsedApexSystemService extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedApexSystemService {
 
     @NonNull
     String getName();
@@ -34,5 +34,4 @@
 
     @Nullable
     String getMaxSdkVersion();
-
 }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
index 8c4d8f0..1e427d0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -19,15 +19,18 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 
-/** @hide **/
+/**
+ * @hide
+ **/
 @DataClass(genGetters = true, genAidl = false, genSetters = true, genParcelable = true)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedApexSystemServiceImpl implements ParsedApexSystemService {
+public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Parcelable {
 
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
     @NonNull
@@ -49,6 +52,7 @@
     }
 
 
+
     // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
@@ -213,8 +217,8 @@
     }
 
     @DataClass.Generated.Member
-    public static final @NonNull android.os.Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
-            = new android.os.Parcelable.Creator<ParsedApexSystemServiceImpl>() {
+    public static final @NonNull Parcelable.Creator<ParsedApexSystemServiceImpl> CREATOR
+            = new Parcelable.Creator<ParsedApexSystemServiceImpl>() {
         @Override
         public ParsedApexSystemServiceImpl[] newArray(int size) {
             return new ParsedApexSystemServiceImpl[size];
@@ -227,10 +231,10 @@
     };
 
     @DataClass.Generated(
-            time = 1638903241144L,
+            time = 1641431950080L,
             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)")
+            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, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
index 3b91f28..1a5d110 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringRes;
-import android.os.Parcelable;
 
 import java.util.List;
 
@@ -28,7 +27,8 @@
  *
  * @hide
  */
-public interface ParsedAttribution extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedAttribution {
 
     /**
      * Maximum length of attribution tag
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
index a4eb4f1..b59f511 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
@@ -35,7 +35,7 @@
  */
 @DataClass(genAidl = false, genSetters = true, genBuilder = false, genParcelable = true)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedAttributionImpl implements ParsedAttribution {
+public class ParsedAttributionImpl implements ParsedAttribution, Parcelable {
 
     /** Maximum amount of attributions per package */
     static final int MAX_NUM_ATTRIBUTIONS = 10000;
@@ -206,10 +206,10 @@
     };
 
     @DataClass.Generated(
-            time = 1627594502974L,
+            time = 1641431950829L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java",
-            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
+            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
index 1a8230d..5b6ecba 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
@@ -21,13 +21,13 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager.Property;
 import android.os.Bundle;
-import android.os.Parcelable;
 
 import java.util.List;
 import java.util.Map;
 
 /** @hide */
-public interface ParsedComponent extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedComponent {
 
     int getBanner();
 
@@ -47,7 +47,7 @@
 
     int getLogo();
 
-    @Nullable
+    @NonNull
     Bundle getMetaData();
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
index 9125e8c..c1a4b2e7 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
@@ -25,28 +25,31 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager.Property;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
-/** @hide */
-@DataClass(genGetters = true, genSetters = true, genConstructor = false, genBuilder = false)
+/**
+ * @hide
+ */
+@DataClass(genGetters = true, genSetters = true, genConstructor = false, genBuilder = false,
+        genParcelable = false)
 @DataClass.Suppress({"setComponentName", "setProperties", "setIntents"})
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public abstract class ParsedComponentImpl implements ParsedComponent {
+public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable {
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
@@ -68,7 +71,7 @@
 
     @NonNull
     @DataClass.PluralOf("intent")
-    private List<ParsedIntentInfo> intents = Collections.emptyList();
+    private List<ParsedIntentInfoImpl> intents = Collections.emptyList();
 
     @Nullable
     private ComponentName componentName;
@@ -95,16 +98,18 @@
         this.flags = other.getFlags();
         this.packageName = other.getPackageName();
         this.componentName = other.getComponentName();
-        this.intents = new ArrayList<>(other.getIntents());
+        this.intents = new ArrayList<>(((ParsedComponentImpl) other).intents);
         this.mProperties = new ArrayMap<>();
         this.mProperties.putAll(other.getProperties());
     }
 
-    public void addIntent(ParsedIntentInfo intent) {
+    public void addIntent(ParsedIntentInfoImpl intent) {
         this.intents = CollectionUtils.add(this.intents, intent);
     }
 
-    /** Add a property to the component */
+    /**
+     * Add a property to the component
+     */
     public void addProperty(@NonNull Property property) {
         this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
     }
@@ -133,6 +138,18 @@
         return componentName;
     }
 
+    @NonNull
+    @Override
+    public Bundle getMetaData() {
+        return metaData == null ? Bundle.EMPTY : metaData;
+    }
+
+    @NonNull
+    @Override
+    public List<ParsedIntentInfo> getIntents() {
+        return new ArrayList<>(intents);
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -149,7 +166,7 @@
         dest.writeInt(this.getDescriptionRes());
         dest.writeInt(this.getFlags());
         sForInternedString.parcel(this.packageName, dest, flags);
-        dest.writeTypedList(this.getIntents());
+        dest.writeTypedList(this.intents);
         dest.writeBundle(this.metaData);
         dest.writeMap(this.mProperties);
     }
@@ -234,16 +251,6 @@
     }
 
     @DataClass.Generated.Member
-    public @NonNull List<ParsedIntentInfo> getIntents() {
-        return intents;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable Bundle getMetaData() {
-        return metaData;
-    }
-
-    @DataClass.Generated.Member
     public @NonNull Map<String,Property> getProperties() {
         return mProperties;
     }
@@ -297,10 +304,10 @@
     }
 
     @DataClass.Generated(
-            time = 1627680195484L,
+            time = 1641414207885L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java",
-            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic  void addIntent(android.content.pm.parsing.component.ParsedIntentInfo)\npublic  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\n  void addIntent(android.content.pm.parsing.component.ParsedIntentInfoImpl)\n  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
index e208854..c6b9b1a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
@@ -21,9 +21,6 @@
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -34,6 +31,9 @@
 import android.util.TypedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 /** @hide */
 class ParsedComponentUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
index a0eae8c..c325d8d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 
 /** @hide */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedInstrumentation extends ParsedComponent {
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
index c8baa9e..23ebdc8 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
@@ -33,7 +33,7 @@
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class ParsedInstrumentationImpl extends ParsedComponentImpl implements
-        ParsedInstrumentation {
+        ParsedInstrumentation, Parcelable {
 
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
@@ -165,10 +165,10 @@
     }
 
     @DataClass.Generated(
-            time = 1627595809880L,
+            time = 1641431951575L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
index 51e1428..c63a689 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -19,7 +19,6 @@
 import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -27,12 +26,15 @@
 import android.content.res.XmlResourceParser;
 
 import com.android.internal.R;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 
-/** @hide */
+/**
+ * @hide
+ */
 public class ParsedInstrumentationUtils {
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
index 57b486a..a7f7b00 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
@@ -19,10 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.IntentFilter;
-import android.os.Parcelable;
 
 /** @hide **/
-public interface ParsedIntentInfo extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedIntentInfo {
 
     boolean isHasDefault();
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
index 1c816da..5b6375d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
@@ -32,7 +32,7 @@
         genBuilder = false, genConstructor = false)
 @DataClass.Suppress({"setIntentFilter"})
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedIntentInfoImpl implements ParsedIntentInfo {
+public class ParsedIntentInfoImpl implements ParsedIntentInfo, Parcelable {
 
     private boolean mHasDefault;
     private int mLabelRes;
@@ -169,10 +169,10 @@
     };
 
     @DataClass.Generated(
-            time = 1627691925408L,
+            time = 1641431952314L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java",
-            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
+            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
index 1e6f630..4f0a504 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -21,9 +21,6 @@
 import android.annotation.NonNull;
 import android.content.Intent;
 import android.content.IntentFilter;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -34,6 +31,9 @@
 import android.util.TypedValue;
 
 import com.android.internal.R;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -49,7 +49,7 @@
     public static final boolean DEBUG = false;
 
     @NonNull
-    public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
+    public static ParseResult<ParsedIntentInfoImpl> parseIntentInfo(String className,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
             boolean allowAutoVerify, ParseInput input)
             throws XmlPullParserException, IOException {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
index 8c1d6c8..b926d53 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
@@ -16,17 +16,20 @@
 
 package com.android.server.pm.pkg.component;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 /** @hide */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedMainComponent extends ParsedComponent {
 
-    @Nullable
+    @NonNull
     String[] getAttributionTags();
 
     /**
      * A main component's name is a class name. This makes code slightly more readable.
      */
+    @NonNull
     String getClassName();
 
     boolean isDirectBootAware();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
index 9b57f48..a2f2f93 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,10 +28,15 @@
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
-/** @hide */
+import libcore.util.EmptyArray;
+
+/**
+ * @hide
+ */
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent {
+public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent,
+        Parcelable {
 
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
@@ -71,6 +77,12 @@
         return getName();
     }
 
+    @NonNull
+    @Override
+    public String[] getAttributionTags() {
+        return attributionTags == null ? EmptyArray.STRING : attributionTags;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -178,51 +190,46 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable String[] getAttributionTags() {
-        return attributionTags;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setDirectBootAware( boolean value) {
+    public @NonNull ParsedMainComponentImpl setDirectBootAware( boolean value) {
         directBootAware = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setEnabled( boolean value) {
+    public @NonNull ParsedMainComponentImpl setEnabled( boolean value) {
         enabled = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setExported( boolean value) {
+    public @NonNull ParsedMainComponentImpl setExported( boolean value) {
         exported = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setOrder( int value) {
+    public @NonNull ParsedMainComponentImpl setOrder( int value) {
         order = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setSplitName(@android.annotation.NonNull String value) {
+    public @NonNull ParsedMainComponentImpl setSplitName(@NonNull String value) {
         splitName = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedMainComponentImpl setAttributionTags(@android.annotation.NonNull String... value) {
+    public @NonNull ParsedMainComponentImpl setAttributionTags(@NonNull String... value) {
         attributionTags = value;
         return this;
     }
 
     @DataClass.Generated(
-            time = 1627324857874L,
+            time = 1641414540422L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
index 2a3e653..f52ad13 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -21,8 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.IntentFilter;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Configuration;
@@ -33,6 +31,8 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -110,13 +110,13 @@
         return input.success(component);
     }
 
-    static ParseResult<ParsedIntentInfo> parseIntentFilter(
+    static ParseResult<ParsedIntentInfoImpl> parseIntentFilter(
             ParsedMainComponent mainComponent,
             ParsingPackage pkg, Resources resources, XmlResourceParser parser,
             boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
             boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
             ParseInput input) throws IOException, XmlPullParserException {
-        ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
+        ParseResult<ParsedIntentInfoImpl> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
                 mainComponent.getName(), pkg, resources, parser, allowGlobs,
                 allowAutoVerify, input);
         if (intentResult.isError()) {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
index 4a6d2c3..dc5347a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
@@ -16,11 +16,13 @@
 
 package com.android.server.pm.pkg.component;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 import java.util.Set;
 
 /** @hide */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedPermission extends ParsedComponent {
 
     @Nullable
@@ -29,7 +31,7 @@
     @Nullable
     String getGroup();
 
-    @Nullable
+    @NonNull
     Set<String> getKnownCerts();
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
index 73b5ffa..64f4fbd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
@@ -17,15 +17,16 @@
 package com.android.server.pm.pkg.component;
 
 /** @hide */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedPermissionGroup extends ParsedComponent {
 
-    int getBackgroundRequestDetailResourceId();
+    int getBackgroundRequestDetailRes();
 
-    int getBackgroundRequestResourceId();
+    int getBackgroundRequestRes();
 
     int getPriority();
 
-    int getRequestDetailResourceId();
+    int getRequestDetailRes();
 
     int getRequestRes();
 }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
index f47fb75..59075de 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
@@ -16,21 +16,25 @@
 
 package com.android.server.pm.pkg.component;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 
-/** @hide */
+/**
+ * @hide
+ */
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
         genAidl = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements
-        ParsedPermissionGroup {
+        ParsedPermissionGroup, Parcelable {
 
-    private int requestDetailResourceId;
-    private int backgroundRequestResourceId;
-    private int backgroundRequestDetailResourceId;
+    private int requestDetailRes;
+    private int backgroundRequestRes;
+    private int backgroundRequestDetailRes;
     private int requestRes;
     private int priority;
 
@@ -43,6 +47,25 @@
     public ParsedPermissionGroupImpl() {
     }
 
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(requestDetailRes);
+        dest.writeInt(backgroundRequestRes);
+        dest.writeInt(backgroundRequestDetailRes);
+        dest.writeInt(requestRes);
+        dest.writeInt(priority);
+    }
+
+    protected ParsedPermissionGroupImpl(@NonNull Parcel in) {
+        super(in);
+        this.requestDetailRes = in.readInt();
+        this.backgroundRequestRes = in.readInt();
+        this.backgroundRequestDetailRes = in.readInt();
+        this.requestRes = in.readInt();
+        this.priority = in.readInt();
+    }
+
 
 
     // Code below generated by codegen v1.0.23.
@@ -51,7 +74,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -60,14 +83,14 @@
 
     @DataClass.Generated.Member
     public ParsedPermissionGroupImpl(
-            int requestDetailResourceId,
-            int backgroundRequestResourceId,
-            int backgroundRequestDetailResourceId,
+            int requestDetailRes,
+            int backgroundRequestRes,
+            int backgroundRequestDetailRes,
             int requestRes,
             int priority) {
-        this.requestDetailResourceId = requestDetailResourceId;
-        this.backgroundRequestResourceId = backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+        this.requestDetailRes = requestDetailRes;
+        this.backgroundRequestRes = backgroundRequestRes;
+        this.backgroundRequestDetailRes = backgroundRequestDetailRes;
         this.requestRes = requestRes;
         this.priority = priority;
 
@@ -75,18 +98,18 @@
     }
 
     @DataClass.Generated.Member
-    public int getRequestDetailResourceId() {
-        return requestDetailResourceId;
+    public int getRequestDetailRes() {
+        return requestDetailRes;
     }
 
     @DataClass.Generated.Member
-    public int getBackgroundRequestResourceId() {
-        return backgroundRequestResourceId;
+    public int getBackgroundRequestRes() {
+        return backgroundRequestRes;
     }
 
     @DataClass.Generated.Member
-    public int getBackgroundRequestDetailResourceId() {
-        return backgroundRequestDetailResourceId;
+    public int getBackgroundRequestDetailRes() {
+        return backgroundRequestDetailRes;
     }
 
     @DataClass.Generated.Member
@@ -100,97 +123,58 @@
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestDetailResourceId( int value) {
-        requestDetailResourceId = value;
+    public @NonNull ParsedPermissionGroupImpl setRequestDetailRes( int value) {
+        requestDetailRes = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestResourceId( int value) {
-        backgroundRequestResourceId = value;
+    public @NonNull ParsedPermissionGroupImpl setBackgroundRequestRes( int value) {
+        backgroundRequestRes = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestDetailResourceId( int value) {
-        backgroundRequestDetailResourceId = value;
+    public @NonNull ParsedPermissionGroupImpl setBackgroundRequestDetailRes( int value) {
+        backgroundRequestDetailRes = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestRes( int value) {
+    public @NonNull ParsedPermissionGroupImpl setRequestRes( int value) {
         requestRes = value;
         return this;
     }
 
     @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroupImpl setPriority( int value) {
+    public @NonNull ParsedPermissionGroupImpl setPriority( int value) {
         priority = value;
         return this;
     }
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        super.writeToParcel(dest, flags);
-
-        dest.writeInt(requestDetailResourceId);
-        dest.writeInt(backgroundRequestResourceId);
-        dest.writeInt(backgroundRequestDetailResourceId);
-        dest.writeInt(requestRes);
-        dest.writeInt(priority);
-    }
-
-    @Override
-    @DataClass.Generated.Member
     public int describeContents() { return 0; }
 
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    protected ParsedPermissionGroupImpl(@android.annotation.NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        super(in);
-
-        int _requestDetailResourceId = in.readInt();
-        int _backgroundRequestResourceId = in.readInt();
-        int _backgroundRequestDetailResourceId = in.readInt();
-        int _requestRes = in.readInt();
-        int _priority = in.readInt();
-
-        this.requestDetailResourceId = _requestDetailResourceId;
-        this.backgroundRequestResourceId = _backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
-        this.requestRes = _requestRes;
-        this.priority = _priority;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @android.annotation.NonNull android.os.Parcelable.Creator<ParsedPermissionGroupImpl> CREATOR
-            = new android.os.Parcelable.Creator<ParsedPermissionGroupImpl>() {
+    public static final @NonNull Parcelable.Creator<ParsedPermissionGroupImpl> CREATOR
+            = new Parcelable.Creator<ParsedPermissionGroupImpl>() {
         @Override
         public ParsedPermissionGroupImpl[] newArray(int size) {
             return new ParsedPermissionGroupImpl[size];
         }
 
         @Override
-        public ParsedPermissionGroupImpl createFromParcel(@android.annotation.NonNull Parcel in) {
+        public ParsedPermissionGroupImpl createFromParcel(@NonNull Parcel in) {
             return new ParsedPermissionGroupImpl(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1627602253988L,
+            time = 1642132854167L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java",
-            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroupImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermissionGroup]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java",
+            inputSignatures = "private  int requestDetailRes\nprivate  int backgroundRequestRes\nprivate  int backgroundRequestDetailRes\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.server.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
index 98007ff..70986c3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -29,13 +29,17 @@
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
 
+import java.util.Collections;
 import java.util.Locale;
 import java.util.Set;
 
-/** @hide */
+/**
+ * @hide
+ */
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission {
+public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission,
+        Parcelable {
 
     private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
 
@@ -56,14 +60,8 @@
     public ParsedPermissionImpl() {
     }
 
-    public ParsedPermissionImpl(ParsedPermission other) {
-        super(other);
-        this.backgroundPermission = other.getBackgroundPermission();
-        this.group = other.getGroup();
-        this.requestRes = other.getRequestRes();
-        this.protectionLevel = other.getProtectionLevel();
-        this.tree = other.isTree();
-        this.parsedPermissionGroup = other.getParsedPermissionGroup();
+    public ParsedPermissionGroup getParsedPermissionGroup() {
+        return parsedPermissionGroup;
     }
 
     public ParsedPermissionImpl setGroup(String group) {
@@ -84,6 +82,12 @@
         }
     }
 
+    @NonNull
+    @Override
+    public Set<String> getKnownCerts() {
+        return knownCerts == null ? Collections.emptySet() : knownCerts;
+    }
+
     public String toString() {
         return "Permission{"
                 + Integer.toHexString(System.identityHashCode(this))
@@ -103,7 +107,7 @@
         dest.writeInt(this.requestRes);
         dest.writeInt(this.protectionLevel);
         dest.writeBoolean(this.tree);
-        dest.writeParcelable(this.parsedPermissionGroup, flags);
+        dest.writeParcelable((ParsedPermissionGroupImpl) this.parsedPermissionGroup, flags);
         sForStringSet.parcel(knownCerts, dest, flags);
     }
 
@@ -115,7 +119,7 @@
         this.protectionLevel = in.readInt();
         this.tree = in.readBoolean();
         this.parsedPermissionGroup = in.readParcelable(ParsedPermissionGroup.class.getClassLoader(),
-                ParsedPermissionGroup.class);
+                ParsedPermissionGroupImpl.class);
         this.knownCerts = sForStringSet.unparcel(in);
     }
 
@@ -155,7 +159,7 @@
             int requestRes,
             int protectionLevel,
             boolean tree,
-            @Nullable ParsedPermissionGroup parsedPermissionGroup,
+            @Nullable ParsedPermissionGroupImpl parsedPermissionGroup,
             @Nullable Set<String> knownCerts) {
         this.backgroundPermission = backgroundPermission;
         this.group = group;
@@ -194,16 +198,6 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable ParsedPermissionGroup getParsedPermissionGroup() {
-        return parsedPermissionGroup;
-    }
-
-    @DataClass.Generated.Member
-    public @Nullable Set<String> getKnownCerts() {
-        return knownCerts;
-    }
-
-    @DataClass.Generated.Member
     public @NonNull ParsedPermissionImpl setBackgroundPermission(@NonNull String value) {
         backgroundPermission = value;
         return this;
@@ -240,10 +234,10 @@
     }
 
     @DataClass.Generated(
-            time = 1627598236506L,
+            time = 1641414649731L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java",
-            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroupImpl parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
index 8562fdf..f2e2f4f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -20,8 +20,6 @@
 
 import android.annotation.NonNull;
 import android.content.pm.PermissionInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -31,13 +29,17 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.List;
 
-/** @hide */
+/**
+ * @hide
+ */
 public class ParsedPermissionUtils {
 
     private static final String TAG = ParsingUtils.TAG;
@@ -107,7 +109,7 @@
                         permission.setKnownCert(knownCert);
                     }
                 }
-                if (permission.getKnownCerts() == null) {
+                if (permission.getKnownCerts().isEmpty()) {
                     Slog.w(TAG, packageName + " defines a knownSigner permission but"
                             + " the provided knownCerts resource is null");
                 }
@@ -228,9 +230,9 @@
             }
 
             // @formatter:off
-            permissionGroup.setRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
-                    .setBackgroundRequestResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
-                    .setBackgroundRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
+            permissionGroup.setRequestDetailRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
+                    .setBackgroundRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
+                    .setBackgroundRequestDetailRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
                     .setRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0))
                     .setPriority(sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0))
                     .setFlags(sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0));
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
index ff391ff..608d08e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
@@ -17,14 +17,15 @@
 package com.android.server.pm.pkg.component;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.content.pm.ApplicationInfo;
-import android.os.Parcelable;
 import android.util.ArrayMap;
 
 import java.util.Set;
 
 /** @hide */
-public interface ParsedProcess extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedProcess {
 
     @NonNull
     Set<String> getDeniedPermissions();
@@ -44,6 +45,7 @@
      * It's a map, because in shared processes, different packages can have different application
      * classes.
      */
+    @SuppressLint("ConcreteCollection")
     @NonNull
     ArrayMap<String, String> getAppClassNamesByPackage();
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
index 96560c7..6d52f65 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
@@ -36,7 +36,7 @@
 @DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
         genBuilder = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedProcessImpl implements ParsedProcess {
+public class ParsedProcessImpl implements ParsedProcess, Parcelable {
 
     @NonNull
     private String name;
@@ -305,10 +305,10 @@
     };
 
     @DataClass.Generated(
-            time = 1639076603310L,
+            time = 1641431953775L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
index d03f153..4f4c2d5 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -18,8 +18,6 @@
 
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -31,6 +29,8 @@
 import com.android.internal.R;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.XmlUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
index 8cc6fc7..ba85e07 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
@@ -16,11 +16,15 @@
 
 package com.android.server.pm.pkg.component;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.PathPermission;
 import android.os.PatternMatcher;
 
+import java.util.List;
+
 /** @hide **/
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedProvider extends ParsedMainComponent {
 
     @Nullable
@@ -30,13 +34,17 @@
 
     boolean isMultiProcess();
 
-    @Nullable PathPermission[] getPathPermissions();
+    @NonNull
+    List<PathPermission> getPathPermissions();
 
-    @Nullable String getReadPermission();
+    @Nullable
+    String getReadPermission();
 
-    @Nullable PatternMatcher[] getUriPermissionPatterns();
+    @NonNull
+    List<PatternMatcher> getUriPermissionPatterns();
 
-    @Nullable String getWritePermission();
+    @Nullable
+    String getWritePermission();
 
     boolean isForceUriPermissions();
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
index e04fc86..e77ecb3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
@@ -28,13 +28,22 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
-/** @hide **/
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @hide
+ **/
 @DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
+@DataClass.Suppress({"setUriPermissionPatterns", "setPathPermissions"})
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedProviderImpl extends ParsedMainComponentImpl implements ParsedProvider {
+public class ParsedProviderImpl extends ParsedMainComponentImpl implements ParsedProvider,
+        Parcelable {
 
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
@@ -50,10 +59,10 @@
     private boolean forceUriPermissions;
     private boolean multiProcess;
     private int initOrder;
-    @Nullable
-    private PatternMatcher[] uriPermissionPatterns;
-    @Nullable
-    private PathPermission[] pathPermissions;
+    @NonNull
+    private List<PatternMatcher> uriPermissionPatterns = Collections.emptyList();
+    @NonNull
+    private List<PathPermission> pathPermissions = Collections.emptyList();
 
     public ParsedProviderImpl(ParsedProvider other) {
         super(other);
@@ -66,8 +75,8 @@
         this.forceUriPermissions = other.isForceUriPermissions();
         this.multiProcess = other.isMultiProcess();
         this.initOrder = other.getInitOrder();
-        this.uriPermissionPatterns = other.getUriPermissionPatterns();
-        this.pathPermissions = other.getPathPermissions();
+        this.uriPermissionPatterns = new ArrayList<>(other.getUriPermissionPatterns());
+        this.pathPermissions = new ArrayList<>(other.getPathPermissions());
     }
 
     public ParsedProviderImpl setReadPermission(String readPermission) {
@@ -84,6 +93,18 @@
         return this;
     }
 
+    @NonNull
+    public ParsedProviderImpl addUriPermissionPattern(@NonNull PatternMatcher value) {
+        uriPermissionPatterns = CollectionUtils.add(uriPermissionPatterns, value);
+        return this;
+    }
+
+    @NonNull
+    public ParsedProviderImpl addPathPermission(@NonNull PathPermission value) {
+        pathPermissions = CollectionUtils.add(pathPermissions, value);
+        return this;
+    }
+
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("Provider{");
@@ -110,8 +131,8 @@
         dest.writeBoolean(this.forceUriPermissions);
         dest.writeBoolean(this.multiProcess);
         dest.writeInt(this.initOrder);
-        dest.writeTypedArray(this.uriPermissionPatterns, flags);
-        dest.writeTypedArray(this.pathPermissions, flags);
+        dest.writeTypedList(this.uriPermissionPatterns, flags);
+        dest.writeTypedList(this.pathPermissions, flags);
     }
 
     public ParsedProviderImpl() {
@@ -127,8 +148,8 @@
         this.forceUriPermissions = in.readBoolean();
         this.multiProcess = in.readBoolean();
         this.initOrder = in.readInt();
-        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
-        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+        this.uriPermissionPatterns = in.createTypedArrayList(PatternMatcher.CREATOR);
+        this.pathPermissions = in.createTypedArrayList(PathPermission.CREATOR);
     }
 
     @NonNull
@@ -153,7 +174,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -170,8 +191,8 @@
             boolean forceUriPermissions,
             boolean multiProcess,
             int initOrder,
-            @Nullable PatternMatcher[] uriPermissionPatterns,
-            @Nullable PathPermission[] pathPermissions) {
+            @NonNull List<PatternMatcher> uriPermissionPatterns,
+            @NonNull List<PathPermission> pathPermissions) {
         this.authority = authority;
         this.syncable = syncable;
         this.readPermission = readPermission;
@@ -181,7 +202,11 @@
         this.multiProcess = multiProcess;
         this.initOrder = initOrder;
         this.uriPermissionPatterns = uriPermissionPatterns;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, uriPermissionPatterns);
         this.pathPermissions = pathPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, pathPermissions);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -227,12 +252,12 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable PatternMatcher[] getUriPermissionPatterns() {
+    public @NonNull List<PatternMatcher> getUriPermissionPatterns() {
         return uriPermissionPatterns;
     }
 
     @DataClass.Generated.Member
-    public @Nullable PathPermission[] getPathPermissions() {
+    public @NonNull List<PathPermission> getPathPermissions() {
         return pathPermissions;
     }
 
@@ -272,23 +297,11 @@
         return this;
     }
 
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setUriPermissionPatterns(@NonNull PatternMatcher... value) {
-        uriPermissionPatterns = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProviderImpl setPathPermissions(@NonNull PathPermission... value) {
-        pathPermissions = value;
-        return this;
-    }
-
     @DataClass.Generated(
-            time = 1627590522169L,
+            time = 1642560323360L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.Nullable android.os.PatternMatcher[] uriPermissionPatterns\nprivate @android.annotation.Nullable android.content.pm.PathPermission[] pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedProviderImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedProvider]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic  com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
index 9d3129b..4cb4dd0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -16,16 +16,14 @@
 
 package com.android.server.pm.pkg.component;
 
-import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
 import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
+import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.IntentFilter;
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -36,6 +34,8 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -173,14 +173,14 @@
             final ParseResult result;
             switch (name) {
                 case "intent-filter":
-                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                    ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils
                             .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
                                     true /*allowGlobs*/, false /*allowAutoVerify*/,
                                     false /*allowImplicitEphemeralVisibility*/,
                                     false /*failOnNoActions*/, input);
                     result = intentResult;
                     if (intentResult.isSuccess()) {
-                        ParsedIntentInfo intent = intentResult.getResult();
+                        ParsedIntentInfoImpl intent = intentResult.getResult();
                         IntentFilter intentFilter = intent.getIntentFilter();
                         provider.setOrder(Math.max(intentFilter.getOrder(), provider.getOrder()));
                         provider.addIntent(intent);
@@ -253,16 +253,7 @@
             }
 
             if (pa != null) {
-                if (provider.getUriPermissionPatterns() == null) {
-                    provider.setUriPermissionPatterns(new PatternMatcher[1]);
-                    provider.getUriPermissionPatterns()[0] = pa;
-                } else {
-                    final int N = provider.getUriPermissionPatterns().length;
-                    PatternMatcher[] newp = new PatternMatcher[N + 1];
-                    System.arraycopy(provider.getUriPermissionPatterns(), 0, newp, 0, N);
-                    newp[N] = pa;
-                    provider.setUriPermissionPatterns(newp);
-                }
+                provider.addUriPermissionPattern(pa);
                 provider.setGrantUriPermissions(true);
             } else {
                 if (RIGID_PARSER) {
@@ -357,16 +348,7 @@
             }
 
             if (pa != null) {
-                if (provider.getPathPermissions() == null) {
-                    provider.setPathPermissions(new PathPermission[1]);
-                    provider.getPathPermissions()[0] = pa;
-                } else {
-                    final int N = provider.getPathPermissions().length;
-                    PathPermission[] newp = new PathPermission[N + 1];
-                    System.arraycopy(provider.getPathPermissions(), 0, newp, 0, N);
-                    newp[N] = pa;
-                    provider.setPathPermissions(newp);
-                }
+                provider.addPathPermission(pa);
             } else {
                 if (RIGID_PARSER) {
                     return input.error(
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
index 11696be..5fc251c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 
 /** @hide **/
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedService extends ParsedMainComponent {
 
     int getForegroundServiceType();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
index 0171c49..c00e01a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
@@ -32,7 +32,8 @@
 /** @hide **/
 @DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedServiceImpl extends ParsedMainComponentImpl implements ParsedService {
+public class ParsedServiceImpl extends ParsedMainComponentImpl implements ParsedService,
+        Parcelable {
 
     private int foregroundServiceType;
     @Nullable
@@ -138,10 +139,10 @@
     }
 
     @DataClass.Generated(
-            time = 1627592563052L,
+            time = 1641431954479L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java",
-            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index 6fe9411..1940eb5 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -23,8 +23,6 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ServiceInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
 import android.content.pm.parsing.result.ParseResult;
@@ -34,6 +32,8 @@
 import android.os.Build;
 
 import com.android.internal.R;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -133,14 +133,14 @@
             final ParseResult parseResult;
             switch (parser.getName()) {
                 case "intent-filter":
-                    ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+                    ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils
                             .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
                                     true /*allowGlobs*/, false /*allowAutoVerify*/,
                                     false /*allowImplicitEphemeralVisibility*/,
                                     false /*failOnNoActions*/, input);
                     parseResult = intentResult;
                     if (intentResult.isSuccess()) {
-                        ParsedIntentInfo intent = intentResult.getResult();
+                        ParsedIntentInfoImpl intent = intentResult.getResult();
                         IntentFilter intentFilter = intent.getIntentFilter();
                         service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder()));
                         service.addIntent(intent);
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
index 8e3401e..e17d1c4 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.pm.PackageInfo;
-import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -30,7 +29,8 @@
  *
  * @hide
  */
-public interface ParsedUsesPermission extends Parcelable {
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ParsedUsesPermission {
 
     /**
      * Strong assertion by a developer that they will never use this permission to derive the
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
index 70d6f24..9b89373 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
@@ -33,7 +33,7 @@
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
         genAidl = false)
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class ParsedUsesPermissionImpl implements ParsedUsesPermission {
+public class ParsedUsesPermissionImpl implements ParsedUsesPermission, Parcelable {
 
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
     @NonNull
@@ -157,10 +157,10 @@
     };
 
     @DataClass.Generated(
-            time = 1627674645598L,
+            time = 1641431955242L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.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 @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index 2d6c616..b92b845 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
@@ -41,6 +42,7 @@
 import android.content.pm.SigningInfo;
 import android.content.pm.overlay.OverlayPaths;
 import android.os.Environment;
+import android.os.PatternMatcher;
 import android.os.UserHandle;
 
 import com.android.internal.util.ArrayUtils;
@@ -651,8 +653,8 @@
         pi.forceUriPermissions = p.isForceUriPermissions();
         pi.multiprocess = p.isMultiProcess();
         pi.initOrder = p.getInitOrder();
-        pi.uriPermissionPatterns = p.getUriPermissionPatterns();
-        pi.pathPermissions = p.getPathPermissions();
+        pi.uriPermissionPatterns = p.getUriPermissionPatterns().toArray(new PatternMatcher[0]);
+        pi.pathPermissions = p.getPathPermissions().toArray(new PathPermission[0]);
         if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
             pi.uriPermissionPatterns = null;
         }
@@ -690,9 +692,11 @@
         ii.sourceDir = pkg.getBaseApkPath();
         ii.publicSourceDir = pkg.getBaseApkPath();
         ii.splitNames = pkg.getSplitNames();
-        ii.splitSourceDirs = pkg.getSplitCodePaths();
-        ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
-        ii.splitDependencies = pkg.getSplitDependencies();
+        ii.splitSourceDirs = pkg.getSplitCodePaths().length == 0 ? null : pkg.getSplitCodePaths();
+        ii.splitPublicSourceDirs = pkg.getSplitCodePaths().length == 0
+                ? null : pkg.getSplitCodePaths();
+        ii.splitDependencies = pkg.getSplitDependencies().size() == 0
+                ? null : pkg.getSplitDependencies();
 
         if (assignUserFields) {
             assignUserFields(pkg, ii, userId);
@@ -734,9 +738,9 @@
         if (pg == null) return null;
 
         PermissionGroupInfo pgi = new PermissionGroupInfo(
-                pg.getRequestDetailResourceId(),
-                pg.getBackgroundRequestResourceId(),
-                pg.getBackgroundRequestDetailResourceId()
+                pg.getRequestDetailRes(),
+                pg.getBackgroundRequestRes(),
+                pg.getBackgroundRequestDetailRes()
         );
 
         assignSharedFieldsForPackageItemInfo(pgi, pg);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 1f21938..84422cd 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -24,6 +24,7 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
@@ -81,11 +82,8 @@
 import com.android.server.pm.pkg.component.ParsedServiceImpl;
 import com.android.server.pm.pkg.component.ParsedUsesPermission;
 import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
+
+import libcore.util.EmptyArray;
 
 import java.security.PublicKey;
 import java.util.Collections;
@@ -107,6 +105,7 @@
  */
 public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, Parcelable {
 
+    private static final SparseArray<int[]> EMPTY_INT_ARRAY_SPARSE_ARRAY = new SparseArray<>();
     public static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
     public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
             ForInternedString.class);
@@ -660,6 +659,7 @@
         return this;
     }
 
+    @SuppressLint("MissingSuperCall")
     @CallSuper
     @Override
     public Object hideAsParsed() {
@@ -1125,7 +1125,8 @@
 //        appInfo.sharedLibraryInfos
 //        appInfo.showUserIcon
         appInfo.splitClassLoaderNames = splitClassLoaderNames;
-        appInfo.splitDependencies = splitDependencies;
+        appInfo.splitDependencies = (splitDependencies == null || splitDependencies.size() == 0)
+                ? null : splitDependencies;
         appInfo.splitNames = splitNames;
         appInfo.storageUuid = mStorageUuid;
         appInfo.targetSandboxVersion = targetSandboxVersion;
@@ -1144,8 +1145,8 @@
         appInfo.setBaseResourcePath(mBaseApkPath);
         appInfo.setCodePath(mPath);
         appInfo.setResourcePath(mPath);
-        appInfo.setSplitCodePaths(splitCodePaths);
-        appInfo.setSplitResourcePaths(splitCodePaths);
+        appInfo.setSplitCodePaths(ArrayUtils.size(splitCodePaths) == 0 ? null : splitCodePaths);
+        appInfo.setSplitResourcePaths(ArrayUtils.size(splitCodePaths) == 0 ? null : splitCodePaths);
         appInfo.setVersionCode(mLongVersionCode);
         appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
         appInfo.setLocaleConfigRes(mLocaleConfigRes);
@@ -1257,20 +1258,20 @@
         dest.writeStringList(this.originalPackages);
         sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
         sForInternedStringList.parcel(this.requestedPermissions, dest, flags);
-        dest.writeTypedList(this.usesPermissions);
+        ParsingUtils.writeParcelableList(dest, this.usesPermissions);
         sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
         sForStringSet.parcel(this.upgradeKeySets, dest, flags);
         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);
-        dest.writeTypedList(this.attributions);
-        dest.writeTypedList(this.permissions);
-        dest.writeTypedList(this.permissionGroups);
-        dest.writeTypedList(this.instrumentations);
+        ParsingUtils.writeParcelableList(dest, this.activities);
+        ParsingUtils.writeParcelableList(dest, this.apexSystemServices);
+        ParsingUtils.writeParcelableList(dest, this.receivers);
+        ParsingUtils.writeParcelableList(dest, this.services);
+        ParsingUtils.writeParcelableList(dest, this.providers);
+        ParsingUtils.writeParcelableList(dest, this.attributions);
+        ParsingUtils.writeParcelableList(dest, this.permissions);
+        ParsingUtils.writeParcelableList(dest, this.permissionGroups);
+        ParsingUtils.writeParcelableList(dest, this.instrumentations);
         sForIntentInfoPairs.parcel(this.preferredActivityFilters, dest, flags);
         dest.writeMap(this.processes);
         dest.writeBundle(this.metaData);
@@ -1906,22 +1907,22 @@
         return queriesProviders;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public String[] getSplitClassLoaderNames() {
-        return splitClassLoaderNames;
+        return splitClassLoaderNames == null ? EmptyArray.STRING : splitClassLoaderNames;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public String[] getSplitCodePaths() {
-        return splitCodePaths;
+        return splitCodePaths == null ? EmptyArray.STRING : splitCodePaths;
     }
 
     @Nullable
     @Override
     public SparseArray<int[]> getSplitDependencies() {
-        return splitDependencies;
+        return splitDependencies == null ? EMPTY_INT_ARRAY_SPARSE_ARRAY : splitDependencies;
     }
 
     @Nullable
@@ -1930,16 +1931,16 @@
         return splitFlags;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public String[] getSplitNames() {
-        return splitNames;
+        return splitNames == null ? EmptyArray.STRING : splitNames;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public int[] getSplitRevisionCodes() {
-        return splitRevisionCodes;
+        return splitRevisionCodes == null ? EmptyArray.INT : splitRevisionCodes;
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index bf7c55f..e8f03ff 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -65,6 +65,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -101,6 +102,7 @@
 import com.android.server.pm.pkg.component.ParsedInstrumentation;
 import com.android.server.pm.pkg.component.ParsedInstrumentationUtils;
 import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
 import com.android.server.pm.pkg.component.ParsedIntentInfoUtils;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.component.ParsedPermission;
@@ -230,6 +232,8 @@
      * of required system property within the overlay tag.
      */
     public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
+    public static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
+
     public static final int PARSE_CHATTY = 1 << 31;
 
     @IntDef(flag = true, prefix = { "PARSE_" }, value = {
@@ -241,6 +245,7 @@
             PARSE_IS_SYSTEM_DIR,
             PARSE_MUST_BE_APK,
             PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
+            PARSE_FRAMEWORK_RES_SPLITS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ParseFlags {}
@@ -289,7 +294,7 @@
                 return new ParsingPackageImpl(packageName, baseApkPath, path, manifestArray);
             }
         });
-        result = parser.parsePackage(input, file, parseFlags);
+        result = parser.parsePackage(input, file, parseFlags, /* frameworkSplits= */ null);
         if (result.isError()) {
             return input.error(result);
         }
@@ -343,26 +348,44 @@
      * not check whether {@code packageFile} has changed since the last parse, it's up to callers to
      * do so.
      */
-    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
-        if (packageFile.isDirectory()) {
-            return parseClusterPackage(input, packageFile, flags);
+    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
+            List<File> frameworkSplits) {
+        if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
+                && frameworkSplits.size() > 0
+                && packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
+            return parseClusterPackage(input, packageFile, frameworkSplits, flags);
+        } else if (packageFile.isDirectory()) {
+            return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
         } else {
             return parseMonolithicPackage(input, packageFile, flags);
         }
     }
 
     /**
-     * Parse all APKs contained in the given directory, treating them as a single package. This also
-     * performs validity checking, such as requiring identical package name and version codes, a
-     * single base APK, and unique split names.
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs validity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Can also be passed the framework-res.apk file and a list of split apks coming from apexes
+     * (via {@code frameworkSplits}) in which case they will be parsed similar to cluster packages
+     * even if they are in different folders. Note that this code path may have other behaviour
+     * differences.
      * <p>
      * Note that this <em>does not</em> perform signature verification; that must be done separately
      * in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
      */
     private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
-            int flags) {
+            List<File> frameworkSplits, int flags) {
+        // parseClusterPackageLite should receive no flags (0) for regular splits but we want to
+        // pass the flags for framework splits
+        int liteParseFlags = 0;
+        if ((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0) {
+            liteParseFlags = flags;
+        }
         final ParseResult<PackageLite> liteResult =
-                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
+                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
+                        liteParseFlags);
         if (liteResult.isError()) {
             return input.error(liteResult);
         }
@@ -1673,7 +1696,7 @@
                 continue;
             }
             if (parser.getName().equals("intent")) {
-                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(
+                ParseResult<ParsedIntentInfoImpl> result = ParsedIntentInfoUtils.parseIntentInfo(
                         null /*className*/, pkg, res, parser, true /*allowGlobs*/,
                         true /*allowAutoVerify*/, input);
                 if (result.isError()) {
@@ -2679,9 +2702,8 @@
             // as it would have already been set when we processed the activity. We wait to
             // process the meta data here since this method is called at the end of processing
             // the application and all meta data is guaranteed.
-            final float activityAspectRatio = activity.getMetaData() != null
-                    ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
-                    : maxAspectRatio;
+            final float activityAspectRatio = activity.getMetaData()
+                    .getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
 
             ComponentMutateUtils.setMaxAspectRatio(activity, activity.getResizeMode(),
                     activityAspectRatio);
@@ -2716,9 +2738,8 @@
         int activitiesSize = activities.size();
         for (int index = 0; index < activitiesSize; index++) {
             ParsedActivity activity = activities.get(index);
-            if (supportsSizeChanges || (activity.getMetaData() != null
-                    && activity.getMetaData().getBoolean(
-                            METADATA_SUPPORTS_SIZE_CHANGES, false))) {
+            if (supportsSizeChanges || activity.getMetaData()
+                    .getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false)) {
                 ComponentMutateUtils.setSupportsSizeChanges(activity, true);
             }
         }
@@ -2991,9 +3012,12 @@
             }
 
             signingDetails = result.getResult();
-
+            final File frameworkRes = new File(Environment.getRootDirectory(),
+                    "framework/framework-res.apk");
+            boolean isFrameworkResSplit = frameworkRes.getAbsolutePath()
+                    .equals(pkg.getBaseApkPath());
             String[] splitCodePaths = pkg.getSplitCodePaths();
-            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            if (!ArrayUtils.isEmpty(splitCodePaths) && !isFrameworkResSplit) {
                 for (int i = 0; i < splitCodePaths.length; i++) {
                     result = getSigningDetails(
                             input,
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 95fec36..9430e98 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -20,8 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.XmlResourceParser;
@@ -32,6 +30,8 @@
 
 import com.android.internal.util.Parcelling;
 import com.android.internal.util.XmlUtils;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -84,7 +84,8 @@
     }
 
     /**
-     * Use with {@link Parcel#writeTypedList(List)}
+     * Use with {@link Parcel#writeTypedList(List)} or
+     * {@link #writeInterfaceAsImplList(Parcel, List)}
      *
      * @see Parcel#createTypedArrayList(Parcelable.Creator)
      */
@@ -103,6 +104,29 @@
         return list;
     }
 
+    /**
+     * Use with {@link #createTypedInterfaceList(Parcel, Parcelable.Creator)}.
+     *
+     * Writes a list that can be cast as Parcelable types at runtime.
+     * TODO: Remove with ImmutableList multi-casting support
+     *
+     * @see Parcel#writeTypedList(List)
+     */
+    @NonNull
+    public static void writeParcelableList(@NonNull Parcel parcel, List<?> list) {
+        if (list == null) {
+            parcel.writeInt(-1);
+            return;
+        }
+        int size = list.size();
+        int index = 0;
+        parcel.writeInt(size);
+        while (index < size) {
+            parcel.writeTypedObject((Parcelable) list.get(index), 0);
+            index++;
+        }
+    }
+
     public static class StringPairListParceler implements
             Parcelling<List<Pair<String, ParsedIntentInfo>>> {
 
@@ -120,7 +144,7 @@
             for (int index = 0; index < size; index++) {
                 Pair<String, ParsedIntentInfo> pair = item.get(index);
                 dest.writeString(pair.first);
-                dest.writeParcelable(pair.second, parcelFlags);
+                dest.writeParcelable((Parcelable) pair.second, parcelFlags);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
index a323e20..2ef90ac 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
@@ -324,13 +324,13 @@
      * @see ApplicationInfo#splitSourceDirs
      * @see ApplicationInfo#getSplitCodePaths
      */
-    @Nullable
+    @NonNull
     String[] getSplitCodePaths();
 
     /**
      * @see ApplicationInfo#splitDependencies
      */
-    @Nullable
+    @NonNull
     SparseArray<int[]> getSplitDependencies();
 
     /**
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java
index 2bc4ee7..78ce6f1 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStatePackageInfo.java
@@ -249,12 +249,13 @@
      * @see ApplicationInfo#splitNames
      * @see PackageInfo#splitNames
      */
-    @Nullable
+    @NonNull
     String[] getSplitNames();
 
     /**
      * @see PackageInfo#splitRevisionCodes
      */
+    @NonNull
     int[] getSplitRevisionCodes();
 
     /**
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 d0b50d27..d6c89f7 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
@@ -60,7 +60,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.PackageUserStateUtils;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
@@ -1751,7 +1751,7 @@
         int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative,
                 userId, debugObject);
         if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) {
-            PackageUserState pkgUserState = pkgSetting.getUserStateOrDefault(userId);
+            PackageUserStateInternal pkgUserState = pkgSetting.getUserStateOrDefault(userId);
             if (!pkgUserState.isInstalled()) {
                 return APPROVAL_LEVEL_NOT_INSTALLED;
             }
@@ -1783,7 +1783,7 @@
             return APPROVAL_LEVEL_UNDECLARED;
         }
 
-        final PackageUserState pkgUserState = pkgSetting.getUserStates().get(userId);
+        final PackageUserStateInternal pkgUserState = pkgSetting.getUserStates().get(userId);
         if (pkgUserState == null) {
             if (DEBUG_APPROVAL) {
                 debugApproval(packageName, debugObject, userId, false,
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b7ca4de..e180032 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -18,9 +18,6 @@
 
 import static android.app.AppOpsManager.OP_FLAG_SELF;
 import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
-import static android.app.usage.NetworkStatsManager.FLAG_POLL_FORCE;
-import static android.app.usage.NetworkStatsManager.FLAG_POLL_ON_OPEN;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -82,6 +79,7 @@
 import android.app.RuntimeAppOpAccessMessage;
 import android.app.StatsManager;
 import android.app.StatsManager.PullAtomMetadata;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.UidTraffic;
@@ -100,8 +98,6 @@
 import android.media.MediaDrm;
 import android.media.UnsupportedSchemeException;
 import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.net.NetworkStats;
@@ -197,6 +193,7 @@
 import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.net.module.util.NetworkStatsUtils;
 import com.android.role.RoleManagerLocal;
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalManagerRegistry;
@@ -244,7 +241,7 @@
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.function.BiConsumer;
+import java.util.function.Function;
 
 /**
  * SystemService containing PullAtomCallbacks that are registered with statsd.
@@ -340,6 +337,7 @@
     private WifiManager mWifiManager;
     private TelephonyManager mTelephony;
     private SubscriptionManager mSubscriptionManager;
+    private NetworkStatsManager mNetworkStatsManager;
 
     @GuardedBy("mKernelWakelockLock")
     private KernelWakelockReader mKernelWakelockReader;
@@ -788,7 +786,7 @@
                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
         mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
         mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
-
+        mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
         // Initialize DiskIO
         mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
 
@@ -981,32 +979,6 @@
         registerOemManagedBytesTransfer();
     }
 
-    /**
-     * Return the {@code INetworkStatsSession} object that holds the necessary properties needed
-     * for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or
-     * null if the service or binder cannot be obtained. Calling this method will trigger poll
-     * in NetworkStatsService with once per 15 seconds rate-limit, unless {@code bypassRateLimit}
-     * is set to true. This is needed in {@link #getUidNetworkStatsSnapshotForTemplate}, where
-     * bypassing the limit is necessary for perfd to supply realtime stats to developers looking at
-     * the network usage of their app.
-     */
-    @Nullable
-    private INetworkStatsSession getNetworkStatsSession(boolean bypassRateLimit) {
-        final INetworkStatsService networkStatsService =
-                INetworkStatsService.Stub.asInterface(
-                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-        if (networkStatsService == null) return null;
-
-        try {
-            return networkStatsService.openSessionForUsageStats(
-                    FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN | (bypassRateLimit ? FLAG_POLL_FORCE
-                            : FLAG_POLL_ON_OPEN), mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Cannot get NetworkStats session", e);
-            return null;
-        }
-    }
-
     private IThermalService getIThermalService() {
         synchronized (mThermalLock) {
             if (mThermalService == null) {
@@ -1137,8 +1109,8 @@
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
                 if (stats != null) {
-                    ret.add(new NetworkStatsExt(stats.groupedByUid(), new int[] {TRANSPORT_WIFI},
-                                    /*slicedByFgbg=*/false));
+                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
+                            new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
                 }
                 break;
             }
@@ -1154,7 +1126,7 @@
                 final NetworkStats stats =
                         getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
                 if (stats != null) {
-                    ret.add(new NetworkStatsExt(stats.groupedByUid(),
+                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
                                     new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
                 }
                 break;
@@ -1245,23 +1217,19 @@
 
     private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
             @NonNull NetworkStatsExt statsExt) {
-        int size = statsExt.stats.size();
-        final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
-        for (int j = 0; j < size; j++) {
-            statsExt.stats.getValues(j, entry);
+        for (NetworkStats.Entry entry : statsExt.stats) {
             StatsEvent statsEvent;
-
             if (statsExt.slicedByFgbg) {
                 // MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom.
                 statsEvent = FrameworkStatsLog.buildStatsEvent(
-                        atomTag, entry.uid,
-                        (entry.set > 0), entry.rxBytes, entry.rxPackets, entry.txBytes,
-                        entry.txPackets);
+                        atomTag, entry.getUid(),
+                        (entry.getSet() > 0), entry.getRxBytes(), entry.getRxPackets(),
+                        entry.getTxBytes(), entry.getTxPackets());
             } else {
                 // MobileBytesTransfer atom or WifiBytesTransfer atom.
                 statsEvent = FrameworkStatsLog.buildStatsEvent(
-                        atomTag, entry.uid, entry.rxBytes,
-                        entry.rxPackets, entry.txBytes, entry.txPackets);
+                        atomTag, entry.getUid(), entry.getRxBytes(),
+                        entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets());
             }
             ret.add(statsEvent);
         }
@@ -1269,13 +1237,12 @@
 
     private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
             @NonNull List<StatsEvent> pulledData) {
-        final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
-        for (int i = 0; i < statsExt.stats.size(); i++) {
-            statsExt.stats.getValues(i, entry);
+        for (NetworkStats.Entry entry : statsExt.stats) {
             pulledData.add(FrameworkStatsLog.buildStatsEvent(
-                    FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.uid,
-                    entry.metered == NetworkStats.METERED_YES, entry.tag, entry.rxBytes,
-                    entry.rxPackets, entry.txBytes, entry.txPackets));
+                    FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(),
+                    entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(),
+                    entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(),
+                    entry.getTxPackets()));
         }
     }
 
@@ -1290,12 +1257,11 @@
         // Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with.
         final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR;
 
-        final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
-        for (int i = 0; i < statsExt.stats.size(); i++) {
-            statsExt.stats.getValues(i, entry);
+        for (NetworkStats.Entry entry : statsExt.stats) {
             pulledData.add(FrameworkStatsLog.buildStatsEvent(
-                    FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, entry.set, entry.rxBytes,
-                    entry.rxPackets, entry.txBytes, entry.txPackets,
+                    FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER,
+                    entry.getSet(), entry.getRxBytes(), entry.getRxPackets(),
+                    entry.getTxBytes(), entry.getTxPackets(),
                     is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType,
                     // Fill information about subscription, these cannot be null since invalid data
                     // would be filtered when adding into subInfo list.
@@ -1309,15 +1275,13 @@
 
     private void addOemDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt,
             @NonNull List<StatsEvent> pulledData) {
-        final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
         final int oemManaged = statsExt.oemManaged;
         for (final int transport : statsExt.transports) {
-            for (int i = 0; i < statsExt.stats.size(); i++) {
-                statsExt.stats.getValues(i, entry);
+            for (NetworkStats.Entry entry : statsExt.stats) {
                 pulledData.add(FrameworkStatsLog.buildStatsEvent(
-                        FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.uid, (entry.set > 0),
-                        oemManaged, transport, entry.rxBytes, entry.rxPackets, entry.txBytes,
-                        entry.txPackets));
+                        FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.getUid(),
+                        (entry.getSet() > 0), oemManaged, transport, entry.getRxBytes(),
+                        entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets()));
             }
         }
     }
@@ -1385,22 +1349,32 @@
         final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
         final long bucketDuration = Settings.Global.getLong(mContext.getContentResolver(),
                 NETSTATS_UID_BUCKET_DURATION, NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS);
-        try {
-            // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
-            //  history when query in every second in order to show realtime statistics. However,
-            //  this is not a good long-term solution since NetworkStatsService will make frequent
-            //  I/O and also block main thread when polling.
-            //  Consider making perfd queries NetworkStatsService directly.
-            final NetworkStats stats = getNetworkStatsSession(template.getMatchRule()
-                    == NetworkTemplate.MATCH_WIFI_WILDCARD).getSummaryForAllUid(template,
-                    currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
-                    currentTimeInMillis, includeTags);
-            return stats;
-        } catch (RemoteException | NullPointerException e) {
-            Slog.e(TAG, "Pulling netstats for template=" + template + " and includeTags="
-                    + includeTags  + " causes error", e);
+
+        // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
+        //  history when query in every second in order to show realtime statistics. However,
+        //  this is not a good long-term solution since NetworkStatsService will make frequent
+        //  I/O and also block main thread when polling.
+        //  Consider making perfd queries NetworkStatsService directly.
+        if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) {
+            mNetworkStatsManager.forceUpdate();
         }
-        return null;
+
+        final android.app.usage.NetworkStats queryNonTaggedStats =
+                mNetworkStatsManager.querySummary(
+                template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+                currentTimeInMillis);
+
+        final NetworkStats nonTaggedStats =
+                NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+        if (!includeTags) return nonTaggedStats;
+
+        final android.app.usage.NetworkStats quaryTaggedStats =
+                mNetworkStatsManager.queryTaggedSummary(template,
+                currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+                currentTimeInMillis);
+        final NetworkStats taggedStats =
+                NetworkStatsUtils.fromPublicNetworkStats(quaryTaggedStats);
+        return nonTaggedStats.add(taggedStats);
     }
 
     @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
@@ -1424,27 +1398,51 @@
         return ret;
     }
 
+    @NonNull private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) {
+        return sliceNetworkStats(stats,
+                (entry) -> {
+                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                                NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
+                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                                NetworkStats.DEFAULT_NETWORK_ALL,
+                                entry.getRxBytes(), entry.getRxPackets(),
+                                entry.getTxBytes(), entry.getTxPackets(), 0);
+                });
+    }
+
     @NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
-                (newEntry, oldEntry) -> {
-                    newEntry.set = oldEntry.set;
+                (entry) -> {
+                        return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
+                                entry.getSet(), NetworkStats.TAG_NONE,
+                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                                NetworkStats.DEFAULT_NETWORK_ALL,
+                                entry.getRxBytes(), entry.getRxPackets(),
+                                entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
     @NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
-                (newEntry, oldEntry) -> {
-                    newEntry.uid = oldEntry.uid;
-                    newEntry.set = oldEntry.set;
+                (entry) -> {
+                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                                entry.getSet(), NetworkStats.TAG_NONE,
+                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                                NetworkStats.DEFAULT_NETWORK_ALL,
+                                entry.getRxBytes(), entry.getRxPackets(),
+                                entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
     @NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
-                (newEntry, oldEntry) -> {
-                    newEntry.uid = oldEntry.uid;
-                    newEntry.tag = oldEntry.tag;
-                    newEntry.metered = oldEntry.metered;
+                (entry) -> {
+                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                                NetworkStats.SET_ALL, entry.getTag(),
+                                entry.getMetered(), NetworkStats.ROAMING_ALL,
+                                NetworkStats.DEFAULT_NETWORK_ALL,
+                                entry.getRxBytes(), entry.getRxPackets(),
+                                entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
@@ -1454,46 +1452,31 @@
      *
      * This function iterates through each NetworkStats.Entry, sets its dimensions equal to the
      * default state (with the presumption that we don't want to slice on anything), and then
-     * applies the slicer lambda to allow users to control which dimensions to slice on. This is
-     * adapted from groupedByUid within NetworkStats.java
+     * applies the slicer lambda to allow users to control which dimensions to slice on.
      *
-     * @param slicer An operation taking into two parameters, new NetworkStats.Entry and old
-     *               NetworkStats.Entry, that should be used to copy state from the old to the new.
+     * @param slicer An operation taking one parameter, NetworkStats.Entry, that should be used to
+     *               get the state from entry to replace the default value.
      *               This is useful for slicing by particular dimensions. For example, if we wished
      *               to slice by uid and tag, we could write the following lambda:
-     *                  (new, old) -> {
-     *                          new.uid = old.uid;
-     *                          new.tag = old.tag;
+     *                  (entry) -> {
+     *                   return new NetworkStats.Entry(null, entry.getUid(),
+     *                           NetworkStats.SET_ALL, entry.getTag(),
+     *                           NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+     *                           NetworkStats.DEFAULT_NETWORK_ALL,
+     *                           entry.getRxBytes(), entry.getRxPackets(),
+     *                           entry.getTxBytes(), entry.getTxPackets(), 0);
      *                  }
-     *               If no slicer is provided, the data is not sliced by any dimensions.
      * @return new NeworkStats object appropriately sliced
      */
     @NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
-            @Nullable BiConsumer<NetworkStats.Entry, NetworkStats.Entry> slicer) {
-        final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
-
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-        entry.uid = NetworkStats.UID_ALL;
-        entry.iface = NetworkStats.IFACE_ALL;
-        entry.set = NetworkStats.SET_ALL;
-        entry.tag = NetworkStats.TAG_NONE;
-        entry.metered = NetworkStats.METERED_ALL;
-        entry.roaming = NetworkStats.ROAMING_ALL;
-        entry.defaultNetwork = NetworkStats.DEFAULT_NETWORK_ALL;
-
-        final NetworkStats.Entry recycle = new NetworkStats.Entry(); // used for retrieving values
-        for (int i = 0; i < stats.size(); i++) {
-            stats.getValues(i, recycle);
+            @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) {
+        NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
+        NetworkStats.Entry entry = new NetworkStats.Entry();
+        for (NetworkStats.Entry e : stats) {
             if (slicer != null) {
-                slicer.accept(entry, recycle);
+                entry = slicer.apply(e);
             }
-
-            entry.rxBytes = recycle.rxBytes;
-            entry.rxPackets = recycle.rxPackets;
-            entry.txBytes = recycle.txBytes;
-            entry.txPackets = recycle.txPackets;
-            // Operations purposefully omitted since we don't use them for statsd.
-            ret.combineValues(entry);
+            ret = ret.addEntry(entry);
         }
         return ret;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 76434c7..49d6a34 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -95,6 +95,7 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
 import static android.os.Build.VERSION_CODES.HONEYCOMB;
 import static android.os.Build.VERSION_CODES.O;
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.COLOR_MODE_DEFAULT;
@@ -635,7 +636,7 @@
     private boolean mOccludesParent;
 
     // The input dispatching timeout for this application token in milliseconds.
-    long mInputDispatchingTimeoutMillis;
+    long mInputDispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 
     private boolean mShowWhenLocked;
     private boolean mInheritShownWhenLocked;
@@ -1443,7 +1444,6 @@
         if (oldParent == null && newParent != null) {
             // First time we are adding the activity to the system.
             mVoiceInteraction = newTask.voiceSession != null;
-            mInputDispatchingTimeoutMillis = getInputDispatchingTimeoutMillisLocked(this);
 
             // TODO(b/36505427): Maybe this call should be moved inside
             // updateOverrideConfiguration()
@@ -1995,6 +1995,7 @@
             task.setRootProcess(proc);
         }
         proc.addActivityIfNeeded(this);
+        mInputDispatchingTimeoutMillis = getInputDispatchingTimeoutMillisLocked(this);
 
         // Update the associated task fragment after setting the process, since it's required for
         // filtering to only report activities that belong to the same process.
@@ -3578,6 +3579,7 @@
             app.removeActivity(this, false /* keepAssociation */);
         }
         app = null;
+        mInputDispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
     }
 
     void makeFinishingLocked() {
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
similarity index 97%
rename from services/core/java/com/android/server/wm/FadeRotationAnimationController.java
rename to services/core/java/com/android/server/wm/AsyncRotationController.java
index 2cefd99..0990f1f 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -35,7 +35,7 @@
  * both fixed rotation and normal rotation to hide some non-activity windows. The caller should show
  * the windows until they are drawn with the new rotation.
  */
-public class FadeRotationAnimationController extends FadeAnimationController {
+public class AsyncRotationController extends FadeAnimationController {
 
     /** The map of window token to its animation leash. */
     private final ArrayMap<WindowToken, SurfaceControl> mTargetWindowTokens = new ArrayMap<>();
@@ -70,7 +70,7 @@
     private final int mOriginalRotation;
     private final boolean mHasScreenRotationAnimation;
 
-    public FadeRotationAnimationController(DisplayContent displayContent) {
+    public AsyncRotationController(DisplayContent displayContent) {
         super(displayContent);
         mService = displayContent.mWmService;
         mOriginalRotation = displayContent.getWindowConfiguration().getRotation();
@@ -81,7 +81,7 @@
         mIsStartTransactionCommitted = !mIsChangeTransition;
         mTimeoutRunnable = displayContent.inTransition() ? () -> {
             synchronized (mService.mGlobalLock) {
-                displayContent.finishFadeRotationAnimationIfPossible();
+                displayContent.finishAsyncRotationIfPossible();
                 mService.mWindowPlacerLocked.performSurfacePlacement();
             }
         } : null;
@@ -277,7 +277,7 @@
                 mIsStartTransactionCommitted = true;
                 if (mPendingShowTokens == null) return;
                 for (int i = mPendingShowTokens.size() - 1; i >= 0; i--) {
-                    mDisplayContent.finishFadeRotationAnimation(mPendingShowTokens.get(i));
+                    mDisplayContent.finishAsyncRotation(mPendingShowTokens.get(i));
                 }
                 mPendingShowTokens = null;
             }
@@ -298,7 +298,7 @@
                 // Only fade in the drawn windows. If the remaining windows are drawn later,
                 // show(WindowToken) will be called to fade in them.
                 if (token.getChildAt(j).isDrawFinishedLw()) {
-                    mDisplayContent.finishFadeRotationAnimation(token);
+                    mDisplayContent.finishAsyncRotation(token);
                     break;
                 }
             }
@@ -320,7 +320,7 @@
             }
             return true;
         }
-        mDisplayContent.finishFadeRotationAnimation(w.mToken);
+        mDisplayContent.finishAsyncRotation(w.mToken);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e449dde..5facc0d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -123,6 +123,7 @@
 import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
 import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
 import static com.android.server.wm.DisplayContentProto.KEEP_CLEAR_AREAS;
+import static com.android.server.wm.DisplayContentProto.MIN_SIZE_OF_RESIZEABLE_TASK_DP;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
@@ -193,13 +194,13 @@
 import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.RotationUtils;
+import android.util.Size;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayCutout;
-import android.view.DisplayCutout.CutoutPathParserInfo;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
@@ -238,7 +239,6 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
 
@@ -323,6 +323,11 @@
      */
     private Rect mLastMirroredDisplayAreaBounds = null;
 
+    /**
+     * The default per Display minimal size of tasks. Calculated at construction.
+     */
+    int mMinSizeOfResizeableTaskDp = -1;
+
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed. We use a subclass of
@@ -547,7 +552,7 @@
 
     /** The delay to avoid toggling the animation quickly. */
     private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
-    private FadeRotationAnimationController mFadeRotationAnimationController;
+    private AsyncRotationController mAsyncRotationController;
 
     final FixedRotationTransitionListener mFixedRotationTransitionListener =
             new FixedRotationTransitionListener();
@@ -578,8 +583,6 @@
     /** Caches the value whether told display manager that we have content. */
     private boolean mLastHasContent;
 
-    private static DisplayRotationUtil sRotationUtil = new DisplayRotationUtil();
-
     /**
      * The input method window for this display.
      */
@@ -1098,7 +1101,7 @@
 
         mInputMonitor = new InputMonitor(mWmService, this);
         mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
-
+        mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
 
         mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
@@ -1554,6 +1557,19 @@
         return config;
     }
 
+    private int getMinimalTaskSizeDp() {
+        final Context displayConfigurationContext =
+                mAtmService.mContext.createConfigurationContext(getConfiguration());
+        final float minimalSize =
+                displayConfigurationContext.getResources().getDimension(
+                                com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        if (Double.compare(mDisplayMetrics.density, 0.0) == 0) {
+            throw new IllegalArgumentException("Display with ID=" + getDisplayId() + "has invalid "
+                + "DisplayMetrics.density= 0.0");
+        }
+        return (int) (minimalSize / mDisplayMetrics.density);
+    }
+
     private boolean updateOrientation(boolean forceUpdate) {
         final int orientation = getOrientation();
         // The last orientation source is valid only after getOrientation.
@@ -1712,8 +1728,8 @@
     }
 
     @VisibleForTesting
-    @Nullable FadeRotationAnimationController getFadeRotationAnimationController() {
-        return mFadeRotationAnimationController;
+    @Nullable AsyncRotationController getAsyncRotationController() {
+        return mAsyncRotationController;
     }
 
     void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
@@ -1723,13 +1739,13 @@
     void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
         if (mFixedRotationLaunchingApp == null && r != null) {
             mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
-            startFadeRotationAnimation(
+            startAsyncRotation(
                     // Delay the hide animation to avoid blinking by clicking navigation bar that
                     // may toggle fixed rotation in a short time.
                     r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
         } else if (mFixedRotationLaunchingApp != null && r == null) {
             mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
-            finishFadeRotationAnimationIfPossible();
+            finishAsyncRotationIfPossible();
         }
         mFixedRotationLaunchingApp = r;
     }
@@ -1842,9 +1858,9 @@
         return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
     }
 
-    private void startFadeRotationAnimationIfNeeded() {
+    private void startAsyncRotationIfNeeded() {
         if (isRotationChanging()) {
-            startFadeRotationAnimation(false /* shouldDebounce */);
+            startAsyncRotation(false /* shouldDebounce */);
         }
     }
 
@@ -1853,12 +1869,12 @@
      *
      * @return {@code true} if the animation is executed right now.
      */
-    private boolean startFadeRotationAnimation(boolean shouldDebounce) {
+    private boolean startAsyncRotation(boolean shouldDebounce) {
         if (shouldDebounce) {
             mWmService.mH.postDelayed(() -> {
                 synchronized (mWmService.mGlobalLock) {
                     if (mFixedRotationLaunchingApp != null
-                            && startFadeRotationAnimation(false /* shouldDebounce */)) {
+                            && startAsyncRotation(false /* shouldDebounce */)) {
                         // Apply the transaction so the animation leash can take effect immediately.
                         getPendingTransaction().apply();
                     }
@@ -1866,28 +1882,28 @@
             }, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
             return false;
         }
-        if (mFadeRotationAnimationController == null) {
-            mFadeRotationAnimationController = new FadeRotationAnimationController(this);
-            mFadeRotationAnimationController.hide();
+        if (mAsyncRotationController == null) {
+            mAsyncRotationController = new AsyncRotationController(this);
+            mAsyncRotationController.hide();
             return true;
         }
         return false;
     }
 
     /** Re-show the previously hidden windows if all seamless rotated windows are done. */
-    void finishFadeRotationAnimationIfPossible() {
-        final FadeRotationAnimationController controller = mFadeRotationAnimationController;
+    void finishAsyncRotationIfPossible() {
+        final AsyncRotationController controller = mAsyncRotationController;
         if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
             controller.show();
-            mFadeRotationAnimationController = null;
+            mAsyncRotationController = null;
         }
     }
 
     /** Shows the given window which may be hidden for screen rotation. */
-    void finishFadeRotationAnimation(WindowToken windowToken) {
-        final FadeRotationAnimationController controller = mFadeRotationAnimationController;
+    void finishAsyncRotation(WindowToken windowToken) {
+        final AsyncRotationController controller = mAsyncRotationController;
         if (controller != null && controller.show(windowToken)) {
-            mFadeRotationAnimationController = null;
+            mAsyncRotationController = null;
         }
     }
 
@@ -1897,7 +1913,7 @@
             // The window should look no different before and after rotation.
             return false;
         }
-        final FadeRotationAnimationController controller = mFadeRotationAnimationController;
+        final AsyncRotationController controller = mAsyncRotationController;
         return controller == null || !controller.isHandledToken(w.mToken);
     }
 
@@ -2077,20 +2093,12 @@
             return WmDisplayCutout.computeSafeInsets(
                     cutout, displayWidth, displayHeight);
         }
-        final Insets waterfallInsets =
-                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
+        final DisplayCutout rotatedCutout =
+                cutout.getRotated(displayWidth, displayHeight, ROTATION_0, rotation);
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final Rect[] newBounds = sRotationUtil.getRotatedBounds(
-                cutout.getBoundingRectsAll(),
-                rotation, displayWidth, displayHeight);
-        final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
-        final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
-                info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
-                info.getCutoutSpec(), rotation, info.getScale());
-        return WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
+        return new WmDisplayCutout(rotatedCutout, new Size(
                 rotated ? displayHeight : displayWidth,
-                rotated ? displayWidth : displayHeight);
+                rotated ? displayWidth : displayHeight));
     }
 
     private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
@@ -2711,6 +2719,7 @@
             //                    layout.
             mInsetsStateController.onDisplayInfoUpdated(false /* notifyInsetsChanged */);
         }
+        mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
         mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
         mDisplayPolicy.onDisplayInfoChanged(info);
     }
@@ -3206,7 +3215,7 @@
         // Hide the windows which are not significant in rotation animation. So that the windows
         // don't need to block the unfreeze time.
         if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
-            startFadeRotationAnimationIfNeeded();
+            startAsyncRotationIfNeeded();
         }
     }
 
@@ -3228,7 +3237,7 @@
             }
             if (!controller.isCollecting(this)) {
                 controller.collect(this);
-                startFadeRotationAnimationIfNeeded();
+                startAsyncRotationIfNeeded();
             }
             return;
         }
@@ -3240,7 +3249,7 @@
                 mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
                 controller.mTransitionMetricsReporter.associate(t,
                         startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
-                startFadeRotationAnimation(false /* shouldDebounce */);
+                startAsyncRotation(false /* shouldDebounce */);
             }
             t.setKnownConfigChanges(this, changes);
         }
@@ -3272,6 +3281,7 @@
             screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
         }
         mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
+        proto.write(MIN_SIZE_OF_RESIZEABLE_TASK_DP, mMinSizeOfResizeableTaskDp);
         if (mTransitionController.isShellTransitionsEnabled()) {
             mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
         } else {
@@ -3349,6 +3359,7 @@
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
         pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
         pw.print("dpi");
+        pw.print(" mMinSizeOfResizeableTaskDp="); pw.print(mMinSizeOfResizeableTaskDp);
         if (mInitialDisplayWidth != mBaseDisplayWidth
                 || mInitialDisplayHeight != mBaseDisplayHeight
                 || mInitialDisplayDensity != mBaseDisplayDensity) {
@@ -3983,8 +3994,8 @@
             mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken);
             // Hide the window until the rotation is done to avoid intermediate artifacts if the
             // parent surface of IME container is changed.
-            if (mFadeRotationAnimationController != null) {
-                mFadeRotationAnimationController.hideImmediately(mInputMethodWindow.mToken);
+            if (mAsyncRotationController != null) {
+                mAsyncRotationController.hideImmediately(mInputMethodWindow.mToken);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 8c8b33f..7387ea3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -638,7 +638,7 @@
         }, true /* traverseTopToBottom */);
         mSeamlessRotationCount = 0;
         mRotatingSeamlessly = false;
-        mDisplayContent.finishFadeRotationAnimationIfPossible();
+        mDisplayContent.finishAsyncRotationIfPossible();
     }
 
     private void prepareSeamlessRotation() {
@@ -729,7 +729,7 @@
                     "Performing post-rotate rotation after seamless rotation");
             // Finish seamless rotation.
             mRotatingSeamlessly = false;
-            mDisplayContent.finishFadeRotationAnimationIfPossible();
+            mDisplayContent.finishAsyncRotationIfPossible();
 
             updateRotationAndSendNewConfigIfChanged();
         }
@@ -1103,6 +1103,21 @@
         return oldRotation != rotation;
     }
 
+
+    /**
+     * Resets whether the screen can be rotated via the accelerometer in all 4 rotations as the
+     * default behavior.
+     *
+     * To be called if there is potential that the value changed. For example if the active display
+     * changed.
+     *
+     * At the moment it is called from
+     * {@link DisplayWindowSettings#applyRotationSettingsToDisplayLocked}.
+     */
+    void resetAllowAllRotations() {
+        mAllowAllRotations = ALLOW_ALL_ROTATIONS_UNDEFINED;
+    }
+
     /**
      * Given an orientation constant, returns the appropriate surface rotation, taking into account
      * sensors, docking mode, rotation lock, and other factors.
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 483c799..70c769d 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -297,6 +297,8 @@
         final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
                 ? settings.mIgnoreOrientationRequest : false;
         dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
+
+        dc.getDisplayRotation().resetAllowAllRotations();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a8a9231..21eea94 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -298,9 +298,9 @@
 
     private Point getWindowFrameSurfacePosition() {
         if (mControl != null) {
-            final FadeRotationAnimationController fadeController =
-                    mWin.mDisplayContent.getFadeRotationAnimationController();
-            if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) {
+            final AsyncRotationController controller =
+                    mWin.mDisplayContent.getAsyncRotationController();
+            if (controller != null && controller.shouldFreezeInsetsPosition(mWin)) {
                 // Use previous position because the fade-out animation runs in old rotation.
                 return mControl.getSurfacePosition();
             }
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index 80f2ab6..0ae119a 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -80,8 +80,8 @@
      * @param show true for fade-in, otherwise for fade-out.
      */
     public void fadeWindowToken(boolean show) {
-        final FadeRotationAnimationController controller =
-                mDisplayContent.getFadeRotationAnimationController();
+        final AsyncRotationController controller =
+                mDisplayContent.getAsyncRotationController();
         final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken,
                 ANIMATION_TYPE_APP_TRANSITION);
         if (controller == null) {
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 49d30cd..36c092b 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -92,7 +92,7 @@
                 || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
                 && displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
                 && service.getRecentsAnimationController() == null
-                && displayContent.getFadeRotationAnimationController() == null;
+                && displayContent.getAsyncRotationController() == null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index f97a48b..1183094 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -614,7 +614,7 @@
     private void attachNavigationBarToApp() {
         if (!mShouldAttachNavBarToAppDuringTransition
                 // Skip the case where the nav bar is controlled by fade rotation.
-                || mDisplayContent.getFadeRotationAnimationController() != null) {
+                || mDisplayContent.getAsyncRotationController() != null) {
             return;
         }
         boolean shouldTranslateNavBar = false;
@@ -701,7 +701,7 @@
     void animateNavigationBarForAppLaunch(long duration) {
         if (!mShouldAttachNavBarToAppDuringTransition
                 // Skip the case where the nav bar is controlled by fade rotation.
-                || mDisplayContent.getFadeRotationAnimationController() != null
+                || mDisplayContent.getAsyncRotationController() != null
                 || mNavigationBarAttachedToApp
                 || mNavBarAttachedApp == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d031bec..4c72d02b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -73,7 +73,6 @@
 import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.KeyguardController.KEYGUARD_SLEEP_TOKEN_TAG;
-import static com.android.server.wm.RootWindowContainerProto.DEFAULT_MIN_SIZE_RESIZABLE_TASK;
 import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
 import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
@@ -111,7 +110,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
@@ -134,7 +132,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
@@ -1220,11 +1217,6 @@
         pw.println(mTopFocusedDisplayId);
     }
 
-    void dumpDefaultMinSizeOfResizableTask(PrintWriter pw) {
-        pw.print("  mDefaultMinSizeOfResizeableTaskDp=");
-        pw.println(mDefaultMinSizeOfResizeableTaskDp);
-    }
-
     void dumpLayoutNeededDisplayIds(PrintWriter pw) {
         if (!isLayoutNeeded()) {
             return;
@@ -1271,7 +1263,6 @@
         mTaskSupervisor.getKeyguardController().dumpDebug(proto, KEYGUARD_CONTROLLER);
         proto.write(IS_HOME_RECENTS_COMPONENT,
                 mTaskSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
-        proto.write(DEFAULT_MIN_SIZE_RESIZABLE_TASK, mDefaultMinSizeOfResizeableTaskDp);
         proto.end(token);
     }
 
@@ -1359,7 +1350,6 @@
                 mDefaultDisplay = displayContent;
             }
         }
-        calculateDefaultMinimalSizeOfResizeableTasks();
 
         final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
         defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
@@ -3477,17 +3467,6 @@
         mService.startLaunchPowerMode(reason);
     }
 
-    // TODO(b/191434136): handle this properly when we add multi-window support on secondary
-    //  display.
-    private void calculateDefaultMinimalSizeOfResizeableTasks() {
-        final Resources res = mService.mContext.getResources();
-        final float minimalSize = res.getDimension(
-                com.android.internal.R.dimen.default_minimal_size_resizable_task);
-        final DisplayMetrics dm = res.getDisplayMetrics();
-
-        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
-    }
-
     /**
      * Dumps the activities matching the given {@param name} in the either the focused root task
      * or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true.
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 7acc0c5..9b94f44 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -45,7 +45,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ShortcutServiceInternal;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Binder;
@@ -222,17 +221,17 @@
 
     @Override
     public int relayout(IWindow window, WindowManager.LayoutParams attrs,
-            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
+            int requestedWidth, int requestedHeight, int viewFlags, int flags,
             ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
+            InsetsSourceControl[] outActiveControls) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, attrs,
-                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
+                requestedWidth, requestedHeight, viewFlags, flags,
                 outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
-                outActiveControls, outSurfaceSize);
+                outActiveControls);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
@@ -252,6 +251,11 @@
     }
 
     @Override
+    public void clearTouchableRegion(IWindow window) {
+        mService.clearTouchableRegion(this, window);
+    }
+
+    @Override
     public void finishDrawing(IWindow window,
             @Nullable SurfaceControl.Transaction postDrawTransaction) {
         if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2331dc4..ff9d9f7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -281,6 +281,8 @@
     // code.
     static final int PERSIST_TASK_VERSION = 1;
 
+    private static final int DEFAULT_MIN_TASK_SIZE_DP = 220;
+
     private float mShadowRadius = 0;
 
     /**
@@ -2052,7 +2054,9 @@
         // so that the user can not render the task fragment too small to manipulate. We don't need
         // to do this for the root pinned task as the bounds are controlled by the system.
         if (!inPinnedWindowingMode()) {
-            final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+            // Use Display specific min sizes when there is one associated with this Task.
+            final int defaultMinSizeDp = mDisplayContent == null
+                    ? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp;
             final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
             final int defaultMinSize = (int) (defaultMinSizeDp * density);
 
@@ -3413,7 +3417,8 @@
         info.isResizeable = isResizeable();
         info.minWidth = mMinWidth;
         info.minHeight = mMinHeight;
-        info.defaultMinSize = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+        info.defaultMinSize = mDisplayContent == null
+                ? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp;
 
         info.positionInParent = getRelativePosition();
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 18df316..ded58f4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -37,7 +37,6 @@
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -72,6 +71,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.animation.Animation;
@@ -156,7 +156,7 @@
     final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
 
     /** The final animation targets derived from participants after promotion. */
-    private ArraySet<WindowContainer> mTargets = null;
+    private ArrayList<WindowContainer> mTargets;
 
     /** The main display running this transition. */
     private DisplayContent mTargetDisplay;
@@ -385,7 +385,7 @@
         // usually only size 1
         final ArraySet<DisplayContent> displays = new ArraySet<>();
         for (int i = mTargets.size() - 1; i >= 0; --i) {
-            final WindowContainer target = mTargets.valueAt(i);
+            final WindowContainer target = mTargets.get(i);
             if (target.getParent() != null) {
                 final SurfaceControl targetLeash = getLeashSurface(target);
                 final SurfaceControl origParent = getOrigParentSurface(target);
@@ -510,10 +510,10 @@
             dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
         }
 
-        final FadeRotationAnimationController fadeRotationController =
-                mTargetDisplay.getFadeRotationAnimationController();
-        if (fadeRotationController != null) {
-            fadeRotationController.onTransitionFinished();
+        final AsyncRotationController asyncRotationController =
+                mTargetDisplay.getAsyncRotationController();
+        if (asyncRotationController != null) {
+            asyncRotationController.onTransitionFinished();
         }
         // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
         // so re-compute in case the IME target is changed after transition.
@@ -655,7 +655,7 @@
 
         // This is non-null only if display has changes. It handles the visible windows that don't
         // need to be participated in the transition.
-        final FadeRotationAnimationController controller = dc.getFadeRotationAnimationController();
+        final AsyncRotationController controller = dc.getAsyncRotationController();
         if (controller != null) {
             controller.setupStartTransaction(transaction);
         }
@@ -745,7 +745,7 @@
 
         if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
                 // Skip the case where the nav bar is controlled by fade rotation.
-                || dc.getFadeRotationAnimationController() != null) {
+                || dc.getAsyncRotationController() != null) {
             return;
         }
 
@@ -818,7 +818,7 @@
         // Search for the home task. If it is supposed to be visible, then the navbar is not at
         // the bottom of the screen, so we need to animate it.
         for (int i = 0; i < mTargets.size(); ++i) {
-            final Task task = mTargets.valueAt(i).asTask();
+            final Task task = mTargets.get(i).asTask();
             if (task == null || !task.isHomeOrRecentsRootTask()) continue;
             animate = task.isVisibleRequested();
             break;
@@ -901,23 +901,8 @@
     private static boolean reportIfNotTop(WindowContainer wc) {
         // Organized tasks need to be reported anyways because Core won't show() their surfaces
         // and we can't rely on onTaskAppeared because it isn't in sync.
-        // Also report wallpaper so it can be handled properly during display change/rotation.
         // TODO(shell-transitions): switch onTaskAppeared usage over to transitions OPEN.
-        return wc.isOrganized() || isWallpaper(wc);
-    }
-
-    /** @return the depth of child within ancestor, 0 if child == ancestor, or -1 if not a child. */
-    private static int getChildDepth(WindowContainer child, WindowContainer ancestor) {
-        WindowContainer parent = child;
-        int depth = 0;
-        while (parent != null) {
-            if (parent == ancestor) {
-                return depth;
-            }
-            parent = parent.getParent();
-            ++depth;
-        }
-        return -1;
+        return wc.isOrganized();
     }
 
     private static boolean isWallpaper(WindowContainer wc) {
@@ -947,61 +932,48 @@
      *
      * @return {@code true} if transition in target can be promoted to its parent.
      */
-    private static boolean canPromote(WindowContainer target, ArraySet<WindowContainer> topTargets,
+    private static boolean canPromote(WindowContainer<?> target, Targets targets,
             ArrayMap<WindowContainer, ChangeInfo> changes) {
-        final WindowContainer parent = target.getParent();
-        final ChangeInfo parentChanges = parent != null ? changes.get(parent) : null;
-        if (parent == null || !parent.canCreateRemoteAnimationTarget()
-                || parentChanges == null || !parentChanges.hasChanged(parent)) {
+        final WindowContainer<?> parent = target.getParent();
+        final ChangeInfo parentChange = changes.get(parent);
+        if (!parent.canCreateRemoteAnimationTarget()
+                || parentChange == null || !parentChange.hasChanged(parent)) {
             ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "      SKIP: %s",
-                    parent == null ? "no parent" : ("parent can't be target " + parent));
+                    "parent can't be target " + parent);
             return false;
         }
         if (isWallpaper(target)) {
             ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "      SKIP: is wallpaper");
             return false;
         }
-        @TransitionInfo.TransitionMode int mode = TRANSIT_NONE;
-        // Go through all siblings of this target to see if any of them would prevent
-        // the target from promoting.
-        siblingLoop:
+
+        final @TransitionInfo.TransitionMode int mode = changes.get(target).getTransitMode(target);
         for (int i = parent.getChildCount() - 1; i >= 0; --i) {
-            final WindowContainer sibling = parent.getChildAt(i);
+            final WindowContainer<?> sibling = parent.getChildAt(i);
+            if (target == sibling) continue;
             ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "      check sibling %s",
                     sibling);
-            // Check if any topTargets are the sibling or within it
-            for (int j = topTargets.size() - 1; j >= 0; --j) {
-                final int depth = getChildDepth(topTargets.valueAt(j), sibling);
-                if (depth < 0) continue;
-                if (depth == 0) {
-                    final int siblingMode = changes.get(sibling).getTransitMode(sibling);
+            final ChangeInfo siblingChange = changes.get(sibling);
+            if (siblingChange == null || !targets.wasParticipated(sibling)) {
+                if (sibling.isVisibleRequested()) {
+                    // Sibling is visible but not animating, so no promote.
                     ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                            "        sibling is a top target with mode %s",
-                            TransitionInfo.modeToString(siblingMode));
-                    if (mode == TRANSIT_NONE) {
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "          no common mode yet, so set it");
-                        mode = siblingMode;
-                    } else if (mode != siblingMode) {
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "          SKIP: common mode mismatch. was %s",
-                                TransitionInfo.modeToString(mode));
-                        return false;
-                    }
-                    continue siblingLoop;
-                } else {
-                    // Sibling subtree may not be promotable.
-                    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                            "        SKIP: sibling contains top target %s",
-                            topTargets.valueAt(j));
+                            "        SKIP: sibling is visible but not part of transition");
                     return false;
                 }
-            }
-            // No other animations are playing in this sibling
-            if (sibling.isVisibleRequested()) {
-                // Sibling is visible but not animating, so no promote.
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "        SKIP: sibling is visible but not part of transition");
+                        "        unrelated invisible sibling %s", sibling);
+                continue;
+            }
+
+            final int siblingMode = siblingChange.getTransitMode(sibling);
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                    "        sibling is a participant with mode %s",
+                    TransitionInfo.modeToString(siblingMode));
+            if (mode != siblingMode) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "          SKIP: common mode mismatch. was %s",
+                        TransitionInfo.modeToString(mode));
                 return false;
             }
         }
@@ -1011,58 +983,40 @@
     /**
      * Go through topTargets and try to promote (see {@link #canPromote}) one of them.
      *
-     * @param topTargets set of just the top-most targets in the hierarchy of participants.
      * @param targets all targets that will be sent to the player.
-     * @return {@code true} if something was promoted.
      */
-    private static boolean tryPromote(ArraySet<WindowContainer> topTargets,
-            ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
-        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  --- Start combine pass ---");
-        // Go through each target until we find one that can be promoted.
-        for (WindowContainer targ : topTargets) {
-            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "    checking %s", targ);
-            if (!canPromote(targ, topTargets, changes)) {
+    private static void tryPromote(Targets targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
+        WindowContainer<?> lastNonPromotableParent = null;
+        // Go through from the deepest target.
+        for (int i = targets.mArray.size() - 1; i >= 0; --i) {
+            final WindowContainer<?> target = targets.mArray.valueAt(i);
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "    checking %s", target);
+            final WindowContainer<?> parent = target.getParent();
+            if (parent == lastNonPromotableParent) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "      SKIP: its sibling was rejected");
                 continue;
             }
-            // No obstructions found to promotion, so promote
-            final WindowContainer parent = targ.getParent();
-            final ChangeInfo parentInfo = changes.get(parent);
-            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                    "      CAN PROMOTE: promoting to parent %s", parent);
-            targets.add(parent);
-
-            // Go through all children of newly-promoted container and remove them from the
-            // top-targets.
-            for (int i = parent.getChildCount() - 1; i >= 0; --i) {
-                final WindowContainer child = parent.getChildAt(i);
-                int idx = targets.indexOf(child);
-                if (idx >= 0) {
-                    final ChangeInfo childInfo = changes.get(child);
-                    if (reportIfNotTop(child)) {
-                        childInfo.mParent = parent;
-                        parentInfo.addChild(child);
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "        keep as target %s", child);
-                    } else {
-                        if (childInfo.mChildren != null) {
-                            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                    "        merging children in from %s: %s", child,
-                                    childInfo.mChildren);
-                            parentInfo.addChildren(childInfo.mChildren);
-                        }
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "        remove from targets %s", child);
-                        targets.removeAt(idx);
-                    }
-                }
-                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "        remove from topTargets %s", child);
-                topTargets.remove(child);
+            if (!canPromote(target, targets, changes)) {
+                lastNonPromotableParent = parent;
+                continue;
             }
-            topTargets.add(parent);
-            return true;
+            if (reportIfNotTop(target)) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "        keep as target %s", target);
+            } else {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "        remove from targets %s", target);
+                targets.remove(i, target);
+            }
+            if (targets.mArray.indexOfValue(parent) < 0) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "      CAN PROMOTE: promoting to parent %s", parent);
+                // The parent has lower depth, so it will be checked in the later iteration.
+                i++;
+                targets.add(parent);
+            }
         }
-        return false;
     }
 
     /**
@@ -1071,22 +1025,15 @@
      */
     @VisibleForTesting
     @NonNull
-    static ArraySet<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants,
+    static ArrayList<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants,
             ArrayMap<WindowContainer, ChangeInfo> changes) {
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                 "Start calculating TransitionInfo based on participants: %s", participants);
 
-        final ArraySet<WindowContainer> topTargets = new ArraySet<>();
-        // The final animation targets which cannot promote to higher level anymore.
-        final ArraySet<WindowContainer> targets = new ArraySet<>();
-
-        final ArrayList<WindowContainer> tmpList = new ArrayList<>();
-
-        // Build initial set of top-level participants by removing any participants that are no-ops
-        // or children of other participants or are otherwise invalid; however, keep around a list
-        // of participants that should always be reported even if they aren't top.
-        for (WindowContainer wc : participants) {
-            // Don't include detached windows.
+        // Add all valid participants to the target container.
+        final Targets targets = new Targets();
+        for (int i = participants.size() - 1; i >= 0; --i) {
+            final WindowContainer<?> wc = participants.valueAt(i);
             if (!wc.isAttached()) {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                         "  Rejecting as detached: %s", wc);
@@ -1101,70 +1048,62 @@
                         "  Rejecting as no-op: %s", wc);
                 continue;
             }
-
-            // Search through ancestors to find the top-most participant (if one exists)
-            WindowContainer topParent = null;
-            tmpList.clear();
-            if (reportIfNotTop(wc)) {
-                tmpList.add(wc);
-            }
-            // Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
-            boolean skipIntermediateReports = isWallpaper(wc);
-            for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
-                if (!p.isAttached() || changes.get(p) == null || !changes.get(p).hasChanged(p)) {
-                    // Again, we're skipping no-ops
-                    break;
-                }
-                if (participants.contains(p)) {
-                    topParent = p;
-                    break;
-                } else if (isWallpaper(p)) {
-                    skipIntermediateReports = true;
-                } else if (reportIfNotTop(p) && !skipIntermediateReports) {
-                    tmpList.add(p);
-                }
-            }
-            if (topParent != null) {
-                // There was an ancestor participant, so don't add wc to targets unless always-
-                // report. Similarly, add any always-report parents along the way.
-                for (int i = 0; i < tmpList.size(); ++i) {
-                    targets.add(tmpList.get(i));
-                    final ChangeInfo info = changes.get(tmpList.get(i));
-                    info.mParent = i < tmpList.size() - 1 ? tmpList.get(i + 1) : topParent;
-                }
-                continue;
-            }
-            // No ancestors in participant-list, so wc is a top target.
             targets.add(wc);
-            topTargets.add(wc);
+            targets.mValidParticipants.add(wc);
         }
+        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Initial targets: %s",
+                targets.mArray);
+        // Combine the targets from bottom to top if possible.
+        tryPromote(targets, changes);
+        // Establish the relationship between the targets and their top changes.
+        populateParentChanges(targets, changes);
 
-        // Populate children lists
-        for (int i = targets.size() - 1; i >= 0; --i) {
-            if (changes.get(targets.valueAt(i)).mParent != null) {
-                changes.get(changes.get(targets.valueAt(i)).mParent).addChild(targets.valueAt(i));
-            }
-        }
-
-        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Initial targets: %s", targets);
-        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Top targets: %s", topTargets);
-
-        // Combine targets by repeatedly going through the topTargets to see if they can be
-        // promoted until there aren't any promotions possible.
-        while (tryPromote(topTargets, targets, changes)) {
-            // Empty on purpose
-        }
-        return targets;
+        final ArrayList<WindowContainer> targetList = targets.getListSortedByZ();
+        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Final targets: %s", targetList);
+        return targetList;
     }
 
-    /** Add any of `members` within `root` to `out` in top-to-bottom z-order. */
-    private static void addMembersInOrder(WindowContainer root, ArraySet<WindowContainer> members,
-            ArrayList<WindowContainer> out) {
-        for (int i = root.getChildCount() - 1; i >= 0; --i) {
-            final WindowContainer child = root.getChildAt(i);
-            addMembersInOrder(child, members, out);
-            if (members.contains(child)) {
-                out.add(child);
+    /** Populates parent to the change info and collects intermediate targets. */
+    private static void populateParentChanges(Targets targets,
+            ArrayMap<WindowContainer, ChangeInfo> changes) {
+        final ArrayList<WindowContainer<?>> intermediates = new ArrayList<>();
+        for (int i = targets.mValidParticipants.size() - 1; i >= 0; --i) {
+            WindowContainer<?> wc = targets.mValidParticipants.get(i);
+            // Go up if the participant has been represented by its parent.
+            while (targets.mArray.indexOfValue(wc) < 0 && wc.getParent() != null) {
+                wc = wc.getParent();
+            }
+            // Wallpaper must belong to the top (regardless of how nested it is in DisplayAreas).
+            final boolean skipIntermediateReports = isWallpaper(wc);
+            intermediates.clear();
+            // Collect the intermediate parents between target and top changed parent.
+            for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
+                final ChangeInfo parentChange = changes.get(p);
+                if (parentChange == null || !parentChange.hasChanged(p)) break;
+                if (parentChange.mParent != null && !skipIntermediateReports) {
+                    changes.get(wc).mParent = p;
+                    // The chain above the parent was processed.
+                    break;
+                }
+                if (targets.mValidParticipants.contains(p)) {
+                    if (skipIntermediateReports) {
+                        changes.get(wc).mParent = p;
+                    } else {
+                        intermediates.add(p);
+                    }
+                    // The parent reaches a participant.
+                    break;
+                } else if (reportIfNotTop(p) && !skipIntermediateReports) {
+                    intermediates.add(p);
+                }
+            }
+            if (intermediates.isEmpty()) continue;
+            // Add any always-report parents along the way.
+            changes.get(wc).mParent = intermediates.get(0);
+            for (int j = 0; j < intermediates.size() - 1; j++) {
+                final WindowContainer<?> intermediate = intermediates.get(j);
+                changes.get(intermediate).mParent = intermediates.get(j + 1);
+                targets.add(intermediate);
             }
         }
     }
@@ -1201,32 +1140,36 @@
     /**
      * Construct a TransitionInfo object from a set of targets and changes. Also populates the
      * root surface.
+     * @param sortedTargets The targets sorted by z-order from top (index 0) to bottom.
      */
     @VisibleForTesting
     @NonNull
     static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
-            ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
+            ArrayList<WindowContainer> sortedTargets,
+            ArrayMap<WindowContainer, ChangeInfo> changes) {
         final TransitionInfo out = new TransitionInfo(type, flags);
 
-        final ArraySet<WindowContainer> appTargets = new ArraySet<>();
-        final ArraySet<WindowContainer> wallpapers = new ArraySet<>();
-        for (int i = targets.size() - 1; i >= 0; --i) {
-            (isWallpaper(targets.valueAt(i)) ? wallpapers : appTargets).add(targets.valueAt(i));
+        WindowContainer<?> topApp = null;
+        for (int i = 0; i < sortedTargets.size(); i++) {
+            final WindowContainer<?> wc = sortedTargets.get(i);
+            if (!isWallpaper(wc)) {
+                topApp = wc;
+                break;
+            }
         }
-
-        // Find the top-most shared ancestor of app targets
-        if (appTargets.isEmpty()) {
+        if (topApp == null) {
             out.setRootLeash(new SurfaceControl(), 0, 0);
             return out;
         }
-        WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
 
+        // Find the top-most shared ancestor of app targets.
+        WindowContainer<?> ancestor = topApp.getParent();
         // Go up ancestor parent chain until all targets are descendants.
         ancestorLoop:
         while (ancestor != null) {
-            for (int i = appTargets.size() - 1; i >= 0; --i) {
-                final WindowContainer wc = appTargets.valueAt(i);
-                if (!wc.isDescendantOf(ancestor)) {
+            for (int i = sortedTargets.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = sortedTargets.get(i);
+                if (!isWallpaper(wc) && !wc.isDescendantOf(ancestor)) {
                     ancestor = ancestor.getParent();
                     continue ancestorLoop;
                 }
@@ -1234,11 +1177,6 @@
             break;
         }
 
-        // Sort targets top-to-bottom in Z. Check ALL targets here in case the display area itself
-        // is animating: then we want to include wallpapers at the right position.
-        ArrayList<WindowContainer> sortedTargets = new ArrayList<>();
-        addMembersInOrder(ancestor, targets, sortedTargets);
-
         // make leash based on highest (z-order) direct child of ancestor with a participant.
         WindowContainer leashReference = sortedTargets.get(0);
         while (leashReference.getParent() != ancestor) {
@@ -1252,14 +1190,6 @@
         t.close();
         out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);
 
-        // add the wallpapers at the bottom
-        for (int i = wallpapers.size() - 1; i >= 0; --i) {
-            final WindowContainer wc = wallpapers.valueAt(i);
-            // If the displayarea itself is animating, then the wallpaper was already added.
-            if (wc.isDescendantOf(ancestor)) break;
-            sortedTargets.add(wc);
-        }
-
         // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
         final int count = sortedTargets.size();
         for (int i = 0; i < count; ++i) {
@@ -1403,7 +1333,6 @@
     static class ChangeInfo {
         // Usually "post" change state.
         WindowContainer mParent;
-        ArraySet<WindowContainer> mChildren;
 
         // State tracking
         boolean mExistenceChanged = false;
@@ -1498,19 +1427,6 @@
             }
             return flags;
         }
-
-        void addChild(@NonNull WindowContainer wc) {
-            if (mChildren == null) {
-                mChildren = new ArraySet<>();
-            }
-            mChildren.add(wc);
-        }
-        void addChildren(@NonNull ArraySet<WindowContainer> wcs) {
-            if (mChildren == null) {
-                mChildren = new ArraySet<>();
-            }
-            mChildren.addAll(wcs);
-        }
     }
 
     /**
@@ -1603,4 +1519,64 @@
             return b.toString();
         }
     }
+
+    /**
+     * The container to represent the depth relation for calculating transition targets. The window
+     * container with larger depth is put at larger index. For the same depth, higher z-order has
+     * larger index.
+     */
+    private static class Targets {
+        /** All targets. Its keys (depth) are sorted in ascending order naturally. */
+        final SparseArray<WindowContainer<?>> mArray = new SparseArray<>();
+        /** The initial participants which have changes. */
+        final ArrayList<WindowContainer<?>> mValidParticipants = new ArrayList<>();
+        /** The targets which were represented by their parent. */
+        private ArrayList<WindowContainer<?>> mRemovedTargets;
+        private int mDepthFactor;
+
+        void add(WindowContainer<?> target) {
+            // The number of slots per depth is larger than the total number of window container,
+            // so the depth score (key) won't have collision.
+            if (mDepthFactor == 0) {
+                mDepthFactor = target.mWmService.mRoot.getTreeWeight() + 1;
+            }
+            int score = target.getPrefixOrderIndex();
+            WindowContainer<?> wc = target;
+            while (wc != null) {
+                final WindowContainer<?> parent = wc.getParent();
+                if (parent != null) {
+                    score += mDepthFactor;
+                }
+                wc = parent;
+            }
+            mArray.put(score, target);
+        }
+
+        void remove(int index, WindowContainer<?> removingTarget) {
+            mArray.removeAt(index);
+            if (mRemovedTargets == null) {
+                mRemovedTargets = new ArrayList<>();
+            }
+            mRemovedTargets.add(removingTarget);
+        }
+
+        boolean wasParticipated(WindowContainer<?> wc) {
+            return mArray.indexOfValue(wc) >= 0
+                    || (mRemovedTargets != null && mRemovedTargets.contains(wc));
+        }
+
+        /** Returns the target list sorted by z-order in ascending order (index 0 is top). */
+        ArrayList<WindowContainer> getListSortedByZ() {
+            final SparseArray<WindowContainer<?>> arrayByZ = new SparseArray<>(mArray.size());
+            for (int i = mArray.size() - 1; i >= 0; --i) {
+                final int zOrder = mArray.keyAt(i) % mDepthFactor;
+                arrayByZ.put(zOrder, mArray.valueAt(i));
+            }
+            final ArrayList<WindowContainer> sortedTargets = new ArrayList<>(arrayByZ.size());
+            for (int i = arrayByZ.size() - 1; i >= 0; --i) {
+                sortedTargets.add(arrayByZ.valueAt(i));
+            }
+            return sortedTargets;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index fc154a8..36bb55e 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -28,9 +28,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.view.DisplayInfo;
-import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.animation.Animation;
 
 import com.android.internal.protolog.common.ProtoLog;
@@ -190,23 +187,6 @@
         setVisible(visible);
     }
 
-    @Override
-    void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) {
-        if (attrs.height == ViewGroup.LayoutParams.MATCH_PARENT
-                || attrs.width == ViewGroup.LayoutParams.MATCH_PARENT) {
-            return;
-        }
-
-        final DisplayInfo displayInfo = win.getDisplayInfo();
-
-        final float layoutScale = Math.max(
-                (float) displayInfo.logicalHeight / (float) attrs.height,
-                (float) displayInfo.logicalWidth / (float) attrs.width);
-        attrs.height = (int) (attrs.height * layoutScale);
-        attrs.width = (int) (attrs.width * layoutScale);
-        attrs.flags |= WindowManager.LayoutParams.FLAG_SCALED;
-    }
-
     boolean hasVisibleNotDrawnWallpaper() {
         if (!isVisible()) return false;
         for (int j = mChildren.size() - 1; j >= 0; --j) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1205dee..11d1983 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -661,6 +661,11 @@
         }
     }
 
+    /** Returns the total number of descendants, including self. */
+    int getTreeWeight() {
+        return mTreeWeight;
+    }
+
     /**
      * @return The index of this element in the hierarchy tree in prefix order.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 026b9e1..3ba332b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2105,6 +2105,19 @@
         Slog.i(tag, s, e);
     }
 
+    void clearTouchableRegion(Session session, IWindow client) {
+        int uid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                WindowState w = windowForClientLocked(session, client, false);
+                w.clearClientTouchableRegion();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
             Rect visibleInsets, Region touchableRegion) {
         int uid = Binder.getCallingUid();
@@ -2130,6 +2143,7 @@
                     }
                     w.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.performSurfacePlacement();
+                    w.getDisplayContent().getInputMonitor().updateInputWindowsLw(true);
 
                     // We need to report touchable region changes to accessibility.
                     if (mAccessibilityController.hasCallbacks()) {
@@ -2180,9 +2194,9 @@
 
     public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility, int flags,
-            long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
+            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
+            InsetsSourceControl[] outActiveControls) {
         Arrays.fill(outActiveControls, null);
         int result = 0;
         boolean configChanged;
@@ -2202,14 +2216,11 @@
                 win.setRequestedSize(requestedWidth, requestedHeight);
             }
 
-            win.setFrameNumber(frameNumber);
-
             int attrChanges = 0;
             int flagChanges = 0;
             int privateFlagChanges = 0;
             if (attrs != null) {
                 displayPolicy.adjustWindowParamsLw(win, attrs);
-                win.mToken.adjustWindowParams(win, attrs);
                 attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
                 attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), uid,
                         pid);
@@ -2497,11 +2508,6 @@
                 displayContent.sendNewConfiguration();
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
-            if (winAnimator.mSurfaceController != null) {
-                win.calculateSurfaceBounds(win.getLayoutingAttrs(
-                        win.getWindowConfiguration().getRotation()), mTmpRect);
-                outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
-            }
             getInsetsSourceControls(win, outActiveControls);
         }
 
@@ -2580,7 +2586,7 @@
         WindowSurfaceController surfaceController;
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
-            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type);
+            surfaceController = winAnimator.createSurfaceLocked();
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -6420,7 +6426,6 @@
         pw.print("  mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
         mRoot.dumpTopFocusedDisplayId(pw);
-        mRoot.dumpDefaultMinSizeOfResizableTask(pw);
         mRoot.forAllDisplays(dc -> {
             final int displayId = dc.getDisplayId();
             final InsetsControlTarget imeLayeringTarget = dc.getImeTarget(IME_TARGET_LAYERING);
@@ -6438,6 +6443,8 @@
                 pw.print("  imeControlTarget in display# "); pw.print(displayId);
                 pw.print(' '); pw.println(imeControlTarget);
             }
+            pw.print("  Minimum task size of display#"); pw.print(displayId);
+            pw.print(' '); pw.print(dc.mMinSizeOfResizeableTaskDp);
         });
         pw.print("  mInTouchMode="); pw.println(mInTouchMode);
         pw.print("  mBlurEnabled="); pw.println(mBlurController.getBlurEnabled());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1f83767..a228d6a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -163,7 +163,6 @@
 import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
 import static com.android.server.wm.WindowStateProto.DESTROYING;
 import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
 import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE;
@@ -380,7 +379,6 @@
      */
     final boolean mForceSeamlesslyRotate;
     SeamlessRotator mPendingSeamlessRotate;
-    long mFinishSeamlessRotateFrameNumber;
 
     private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -654,6 +652,7 @@
 
     private final Rect mTmpRect = new Rect();
     private final Point mTmpPoint = new Point();
+    private final Region mTmpRegion = new Region();
 
     private final Transaction mTmpTransaction;
 
@@ -706,11 +705,6 @@
      */
     private PowerManagerWrapper mPowerManagerWrapper;
 
-    /**
-     * A frame number in which changes requested in this layout will be rendered.
-     */
-    private long mFrameNumber = -1;
-
     private static final StringBuilder sTmpSB = new StringBuilder();
 
     /**
@@ -969,7 +963,6 @@
         }
 
         mPendingSeamlessRotate.finish(t, this);
-        mFinishSeamlessRotateFrameNumber = getFrameNumber();
         mPendingSeamlessRotate = null;
 
         getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
@@ -1418,8 +1411,8 @@
         return mWindowFrames.mParentFrame;
     }
 
-    void getCompatFrameSize(Rect outFrame) {
-        outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
+    Rect getCompatFrame() {
+        return mWindowFrames.mCompatFrame;
     }
 
     WindowManager.LayoutParams getAttrs() {
@@ -1563,7 +1556,7 @@
             }
         } else {
             // The orientation change is completed. If it was hidden by the animation, reshow it.
-            mDisplayContent.finishFadeRotationAnimation(mToken);
+            mDisplayContent.finishAsyncRotation(mToken);
         }
     }
 
@@ -1608,6 +1601,15 @@
         return getDisplayContent().getDisplayInfo();
     }
 
+    @Override
+    public Rect getMaxBounds() {
+        final Rect maxBounds = mToken.getFixedRotationTransformMaxBounds();
+        if (maxBounds != null) {
+            return maxBounds;
+        }
+        return super.getMaxBounds();
+    }
+
     /**
      * Returns the insets state for the window. Its sources may be the copies with visibility
      * modification according to the state of transient bars.
@@ -2773,6 +2775,7 @@
                 region.set(-dw, -dh, dw + dw, dh + dh);
             }
             subtractTouchExcludeRegionIfNeeded(region);
+
         } else {
             // Not modal
             getTouchableRegion(region);
@@ -2783,6 +2786,14 @@
         if (frame.left != 0 || frame.top != 0) {
             region.translate(-frame.left, -frame.top);
         }
+        if (modal && mTouchableInsets == TOUCHABLE_INSETS_REGION) {
+            // The client gave us a touchable region and so first
+            // we calculate the untouchable region, then punch that out of our
+            // expanded modal region.
+            mTmpRegion.set(0, 0, frame.right, frame.bottom);
+            mTmpRegion.op(mGivenTouchableRegion, Region.Op.DIFFERENCE);
+            region.op(mTmpRegion, Region.Op.DIFFERENCE);
+        }
 
         // TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
         // scaling but the existing logic doesn't expect that. The result is that the already-
@@ -4113,7 +4124,6 @@
         proto.write(IS_ON_SCREEN, isOnScreen());
         proto.write(IS_VISIBLE, isVisible);
         proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
-        proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
         proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4255,7 +4265,6 @@
         } else {
             pw.print("null");
         }
-        pw.println(" finishedFrameNumber=" + mFinishSeamlessRotateFrameNumber);
 
         if (mHScale != 1 || mVScale != 1) {
             pw.println(prefix + "mHScale=" + mHScale
@@ -5624,16 +5633,6 @@
         return target != null ? target.getWindow() : null;
     }
 
-    long getFrameNumber() {
-        // Return the frame number in which changes requested in this layout will be rendered or
-        // -1 if we do not expect the frame to be rendered.
-        return getFrame().isEmpty() ? -1 : mFrameNumber;
-    }
-
-    void setFrameNumber(long frameNumber) {
-        mFrameNumber = frameNumber;
-    }
-
     void forceReportingResized() {
         mWindowFrames.forceReportingResized();
     }
@@ -5820,10 +5819,10 @@
 
         boolean skipLayout = false;
         // Control the timing to switch the appearance of window with different rotations.
-        final FadeRotationAnimationController fadeRotationController =
-                mDisplayContent.getFadeRotationAnimationController();
-        if (fadeRotationController != null
-                && fadeRotationController.handleFinishDrawing(this, postDrawTransaction)) {
+        final AsyncRotationController asyncRotationController =
+                mDisplayContent.getAsyncRotationController();
+        if (asyncRotationController != null
+                && asyncRotationController.handleFinishDrawing(this, postDrawTransaction)) {
             // Consume the transaction because the controller will apply it with fade animation.
             // Layout is not needed because the window will be hidden by the fade leash. Clear
             // sync state because its sync transaction doesn't need to be merged to sync group.
@@ -5904,39 +5903,6 @@
         mRedrawForSyncReported = false;
     }
 
-    void calculateSurfaceBounds(WindowManager.LayoutParams attrs, Rect outSize) {
-        outSize.setEmpty();
-        if ((attrs.flags & FLAG_SCALED) != 0) {
-            // For a scaled surface, we always want the requested size.
-            outSize.right = mRequestedWidth;
-            outSize.bottom = mRequestedHeight;
-        } else {
-            // When we're doing a drag-resizing, request a surface that's fullscreen size,
-            // so that we don't need to reallocate during the process. This also prevents
-            // buffer drops due to size mismatch.
-            if (isDragResizing()) {
-                final DisplayInfo displayInfo = getDisplayInfo();
-                outSize.right = displayInfo.logicalWidth;
-                outSize.bottom = displayInfo.logicalHeight;
-            } else {
-                getCompatFrameSize(outSize);
-            }
-        }
-
-        // This doesn't necessarily mean that there is an error in the system. The sizes might be
-        // incorrect, because it is before the first layout or draw.
-        if (outSize.width() < 1) {
-            outSize.right = 1;
-        }
-        if (outSize.height() < 1) {
-            outSize.bottom = 1;
-        }
-
-        // Adjust for surface insets.
-        outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
-                -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
-    }
-
     /**
      * This method is used to control whether we return the BLAST_SYNC flag
      * from relayoutWindow calls on this window (triggering the client to redirect
@@ -6062,4 +6028,9 @@
         }
         mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
     }
+
+    void clearClientTouchableRegion() {
+        mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+        mGivenTouchableRegion.setEmpty();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 316051e..c17961e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -150,8 +150,6 @@
 
     int mAttrType;
 
-    private final Rect mTmpSize = new Rect();
-
     /**
      * Handles surface changes synchronized to after the client has drawn the surface. This
      * transaction is currently used to reparent the old surface children to the new surface once
@@ -291,7 +289,7 @@
         }
     }
 
-    WindowSurfaceController createSurfaceLocked(int windowType) {
+    WindowSurfaceController createSurfaceLocked() {
         final WindowState w = mWin;
 
         if (mSurfaceController != null) {
@@ -319,16 +317,9 @@
             flags |= SurfaceControl.SKIP_SCREENSHOT;
         }
 
-        w.calculateSurfaceBounds(attrs, mTmpSize);
-
-        final int width = mTmpSize.width();
-        final int height = mTmpSize.height();
-
         if (DEBUG_VISIBILITY) {
             Slog.v(TAG, "Creating surface in session "
                     + mSession.mSurfaceSession + " window " + this
-                    + " w=" + width + " h=" + height
-                    + " x=" + mTmpSize.left + " y=" + mTmpSize.top
                     + " format=" + attrs.format + " flags=" + flags);
         }
 
@@ -339,8 +330,8 @@
             final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
             final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
 
-            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
-                    height, format, flags, this, windowType);
+            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
+                    flags, this, attrs.type);
             mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
                     & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
 
@@ -372,8 +363,7 @@
         if (SHOW_LIGHT_TRANSACTIONS) {
             Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
             WindowManagerService.logSurface(w, "CREATE pos=("
-                    + w.getFrame().left + "," + w.getFrame().top + ") ("
-                    + width + "x" + height + ")" + " HIDE", false);
+                    + w.getFrame().left + "," + w.getFrame().top + ") HIDE", false);
         }
 
         mLastHidden = true;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index fa0d708..665857c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -31,7 +31,6 @@
 import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
-import android.graphics.Region;
 import android.os.Debug;
 import android.os.Trace;
 import android.util.Slog;
@@ -76,8 +75,8 @@
     // Used to track whether we have called detach children on the way to invisibility.
     boolean mChildrenDetached;
 
-    WindowSurfaceController(String name, int w, int h, int format,
-            int flags, WindowStateAnimator animator, int windowType) {
+    WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
+            int windowType) {
         mAnimator = animator;
 
         title = name;
@@ -91,7 +90,6 @@
         final SurfaceControl.Builder b = win.makeSurface()
                 .setParent(win.getSurfaceControl())
                 .setName(name)
-                .setBufferSize(w, h)
                 .setFormat(format)
                 .setFlags(flags)
                 .setMetadata(METADATA_WINDOW_TYPE, windowType)
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e5a3b7a..6d8203c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -430,6 +430,13 @@
         return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayFrames : null;
     }
 
+    Rect getFixedRotationTransformMaxBounds() {
+        return isFixedRotationTransforming()
+                ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration
+                .getMaxBounds()
+                : null;
+    }
+
     Rect getFixedRotationTransformDisplayBounds() {
         return isFixedRotationTransforming()
                 ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration
@@ -646,14 +653,6 @@
         }
     }
 
-    /**
-     * Gives a chance to this {@link WindowToken} to adjust the {@link
-     * android.view.WindowManager.LayoutParams} of its windows.
-     */
-    void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) {
-    }
-
-
     @CallSuper
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
deleted file mode 100644
index 59abaab..0000000
--- a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.utils;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Utility to compute bounds after rotating the screen.
- */
-public class DisplayRotationUtil {
-    private final Matrix mTmpMatrix = new Matrix();
-
-    private static int getRotationToBoundsOffset(int rotation) {
-        switch (rotation) {
-            case ROTATION_0:
-                return 0;
-            case ROTATION_90:
-                return -1;
-            case ROTATION_180:
-                return 2;
-            case ROTATION_270:
-                return 1;
-            default:
-                // should not happen
-                return 0;
-        }
-    }
-
-    @VisibleForTesting
-    static int getBoundIndexFromRotation(int i, int rotation) {
-        return Math.floorMod(i + getRotationToBoundsOffset(rotation),
-                BOUNDS_POSITION_LENGTH);
-    }
-
-    /**
-     * Compute bounds after rotating the screen.
-     *
-     * @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements.
-     * @param rotation rotation constant defined in android.view.Surface.
-     * @param initialDisplayWidth width of the display before the rotation.
-     * @param initialDisplayHeight height of the display before the rotation.
-     * @return Bounds after the rotation.
-     *
-     * @hide
-     */
-    public Rect[] getRotatedBounds(
-            Rect[] bounds, int rotation, int initialDisplayWidth, int initialDisplayHeight) {
-        if (bounds.length != BOUNDS_POSITION_LENGTH) {
-            throw new IllegalArgumentException(
-                    "bounds must have exactly 4 elements: bounds=" + bounds);
-        }
-        if (rotation == ROTATION_0) {
-            return bounds;
-        }
-        transformPhysicalToLogicalCoordinates(rotation, initialDisplayWidth, initialDisplayHeight,
-                mTmpMatrix);
-        Rect[] newBounds = new Rect[BOUNDS_POSITION_LENGTH];
-        for (int i = 0; i < bounds.length; i++) {
-
-            final Rect rect = bounds[i];
-            if (!rect.isEmpty()) {
-                final RectF rectF = new RectF(rect);
-                mTmpMatrix.mapRect(rectF);
-                rectF.round(rect);
-            }
-            newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
-        }
-        return newBounds;
-    }
-}
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index ee3f4f4d..a45771e 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -19,7 +19,6 @@
 import android.graphics.Rect;
 import android.util.Size;
 import android.view.DisplayCutout;
-import android.view.Gravity;
 
 import java.util.Objects;
 
@@ -52,7 +51,7 @@
             return NO_CUTOUT;
         }
         final Size displaySize = new Size(displayWidth, displayHeight);
-        final Rect safeInsets = computeSafeInsets(displaySize, inner);
+        final Rect safeInsets = DisplayCutout.computeSafeInsets(displayWidth, displayHeight, inner);
         return new WmDisplayCutout(inner.replaceSafeInsets(safeInsets), displaySize);
     }
 
@@ -66,45 +65,6 @@
         return computeSafeInsets(mInner, width, height);
     }
 
-    private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
-        if (displaySize.getWidth() == displaySize.getHeight()) {
-            throw new UnsupportedOperationException("not implemented: display=" + displaySize +
-                    " cutout=" + cutout);
-        }
-
-        int leftInset = Math.max(cutout.getWaterfallInsets().left,
-                findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
-        int topInset = Math.max(cutout.getWaterfallInsets().top,
-                findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
-        int rightInset = Math.max(cutout.getWaterfallInsets().right,
-                findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
-        int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
-                findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
-                        Gravity.BOTTOM));
-
-        return new Rect(leftInset, topInset, rightInset, bottomInset);
-    }
-
-    private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
-        if (boundingRect.isEmpty()) {
-            return 0;
-        }
-
-        int inset = 0;
-        switch (gravity) {
-            case Gravity.TOP:
-                return Math.max(inset, boundingRect.bottom);
-            case Gravity.BOTTOM:
-                return Math.max(inset, display.getHeight() - boundingRect.top);
-            case Gravity.LEFT:
-                return Math.max(inset, boundingRect.right);
-            case Gravity.RIGHT:
-                return Math.max(inset, display.getWidth() - boundingRect.left);
-            default:
-                throw new IllegalArgumentException("unknown gravity: " + gravity);
-        }
-    }
-
     public DisplayCutout getDisplayCutout() {
         return mInner;
     }
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index baf2ede..574dbfd 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -38,7 +38,8 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
-                <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/>
+                <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
+                            maxOccurs="1"/>
                 <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
                 <xs:element type="nonNegativeDecimal" name="screenBrightnessRampFastDecrease">
                     <xs:annotation name="final"/>
@@ -67,6 +68,19 @@
                 <xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonShort">
                     <xs:annotation name="final"/>
                 </xs:element>
+
+                <!-- Set of thresholds that dictate the change needed for screen brightness
+                adaptations -->
+                <xs:element type="thresholds" name="displayBrightnessChangeThresholds">
+                    <xs:annotation name="nonnull"/>
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <!-- Set of thresholds that dictate the change needed for ambient brightness
+                adaptations -->
+                <xs:element type="thresholds" name="ambientBrightnessChangeThresholds">
+                    <xs:annotation name="nonnull"/>
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
@@ -81,7 +95,8 @@
 
     <xs:complexType name="highBrightnessMode">
         <xs:all>
-            <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1" maxOccurs="1">
+            <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1"
+                        maxOccurs="1">
                 <xs:annotation name="nonnull"/>
                 <xs:annotation name="final"/>
             </xs:element>
@@ -110,7 +125,8 @@
 
     <xs:complexType name="hbmTiming">
         <xs:all>
-            <xs:element name="timeWindowSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1">
+            <xs:element name="timeWindowSecs" type="xs:nonNegativeInteger" minOccurs="1"
+                        maxOccurs="1">
                 <xs:annotation name="nonnull"/>
                 <xs:annotation name="final"/>
             </xs:element>
@@ -216,5 +232,33 @@
                 <xs:annotation name="final"/>
             </xs:element>
         </xs:sequence>
+      </xs:complexType>
+
+    <!-- Thresholds for brightness changes. -->
+    <xs:complexType name="thresholds">
+        <xs:sequence>
+            <!-- Brightening thresholds. -->
+            <xs:element name="brighteningThresholds" type="brightnessThresholds" minOccurs="0"
+                        maxOccurs="1" >
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <!-- Darkening thresholds. -->
+            <xs:element name="darkeningThresholds" type="brightnessThresholds" minOccurs="0"
+                        maxOccurs="1" >
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
     </xs:complexType>
+
+    <!-- Brightening and darkening minimum change thresholds. -->
+    <xs:complexType name="brightnessThresholds">
+        <!-- Minimum brightness change needed. -->
+        <xs:element name="minimum" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1" >
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+    </xs:complexType>
+
 </xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 6f97431..04f0916 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -1,6 +1,12 @@
 // Signature format: 2.0
 package com.android.server.display.config {
 
+  public class BrightnessThresholds {
+    ctor public BrightnessThresholds();
+    method @NonNull public final java.math.BigDecimal getMinimum();
+    method public final void setMinimum(@NonNull java.math.BigDecimal);
+  }
+
   public class Density {
     ctor public Density();
     method @NonNull public final java.math.BigInteger getDensity();
@@ -18,9 +24,11 @@
 
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
+    method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
     method public final java.math.BigInteger getAmbientLightHorizonLong();
     method public final java.math.BigInteger getAmbientLightHorizonShort();
     method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
+    method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
     method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -31,9 +39,11 @@
     method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+    method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
     method public final void setAmbientLightHorizonLong(java.math.BigInteger);
     method public final void setAmbientLightHorizonShort(java.math.BigInteger);
     method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
+    method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
     method public final void setProxSensor(com.android.server.display.config.SensorDetails);
@@ -121,6 +131,14 @@
     enum_constant public static final com.android.server.display.config.ThermalStatus shutdown;
   }
 
+  public class Thresholds {
+    ctor public Thresholds();
+    method @NonNull public final com.android.server.display.config.BrightnessThresholds getBrighteningThresholds();
+    method @NonNull public final com.android.server.display.config.BrightnessThresholds getDarkeningThresholds();
+    method public final void setBrighteningThresholds(@NonNull com.android.server.display.config.BrightnessThresholds);
+    method public final void setDarkeningThresholds(@NonNull com.android.server.display.config.BrightnessThresholds);
+  }
+
   public class XmlParser {
     ctor public XmlParser();
     method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index f19202a..2090ab3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -32,6 +32,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.PasswordPolicy;
+import android.app.admin.PreferentialNetworkServiceConfig;
 import android.graphics.Color;
 import android.os.Bundle;
 import android.os.PersistableBundle;
@@ -306,6 +307,8 @@
     public boolean mAdminCanGrantSensorsPermissions;
     public boolean mPreferentialNetworkServiceEnabled =
             DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT;
+    public PreferentialNetworkServiceConfig mPreferentialNetworkServiceConfig =
+            PreferentialNetworkServiceConfig.DEFAULT;
 
     private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
     boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 9b87b9d..200b120 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -171,11 +171,11 @@
     public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
 
     @Override
-    public void resetDrawables(@NonNull int[] drawableIds){}
+    public void resetDrawables(@NonNull String[] drawableIds){}
 
     @Override
     public ParcelableResource getDrawable(
-            int drawableId, int drawableStyle, int drawableSource) {
+            String drawableId, String drawableStyle, String drawableSource) {
         return null;
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
index 9a98235..e70c071 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.devicepolicy;
 
-import static android.app.admin.DevicePolicyResources.Drawable.Source.UPDATABLE_DRAWABLE_SOURCES;
-import static android.app.admin.DevicePolicyResources.Drawable.Style;
-import static android.app.admin.DevicePolicyResources.Drawable.Style.UPDATABLE_DRAWABLE_STYLES;
-import static android.app.admin.DevicePolicyResources.Drawable.UPDATABLE_DRAWABLE_IDS;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.UPDATABLE_DRAWABLE_SOURCES;
+import static android.app.admin.DevicePolicyResources.Drawables.Style;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.UPDATABLE_DRAWABLE_STYLES;
+import static android.app.admin.DevicePolicyResources.Drawables.UPDATABLE_DRAWABLE_IDS;
 import static android.app.admin.DevicePolicyResources.Strings.UPDATABLE_STRING_IDS;
 
 import static java.util.Objects.requireNonNull;
@@ -27,7 +27,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyDrawableResource;
-import android.app.admin.DevicePolicyResources;
+import android.app.admin.DevicePolicyResources.Drawables;
 import android.app.admin.DevicePolicyStringResource;
 import android.app.admin.ParcelableResource;
 import android.os.Environment;
@@ -72,13 +72,13 @@
     /**
      * Map of <drawable_id, <style_id, resource_value>>
      */
-    private final Map<Integer, Map<Integer, ParcelableResource>>
+    private final Map<String, Map<String, ParcelableResource>>
             mUpdatedDrawablesForStyle = new HashMap<>();
 
     /**
      * Map of <drawable_id, <source_id, resource_value>>
      */
-    private final Map<Integer, Map<Integer, ParcelableResource>>
+    private final Map<String, Map<String, ParcelableResource>>
             mUpdatedDrawablesForSource = new HashMap<>();
 
     /**
@@ -103,14 +103,17 @@
     boolean updateDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
         boolean updated = false;
         for (int i = 0; i < drawables.size(); i++) {
-            int drawableId = drawables.get(i).getDrawableId();
-            int drawableStyle = drawables.get(i).getDrawableStyle();
-            int drawableSource = drawables.get(i).getDrawableSource();
+            String drawableId = drawables.get(i).getDrawableId();
+            String drawableStyle = drawables.get(i).getDrawableStyle();
+            String drawableSource = drawables.get(i).getDrawableSource();
             ParcelableResource resource = drawables.get(i).getResource();
 
+            Objects.requireNonNull(drawableId, "drawableId must be provided.");
+            Objects.requireNonNull(drawableStyle, "drawableStyle must be provided.");
+            Objects.requireNonNull(drawableSource, "drawableSource must be provided.");
             Objects.requireNonNull(resource, "ParcelableResource must be provided.");
 
-            if (drawableSource == DevicePolicyResources.Drawable.Source.UNDEFINED) {
+            if (Drawables.Source.UNDEFINED.equals(drawableSource)) {
                 updated |= updateDrawable(drawableId, drawableStyle, resource);
             } else {
                 updated |= updateDrawableForSource(drawableId, drawableSource, resource);
@@ -126,7 +129,7 @@
     }
 
     private boolean updateDrawable(
-            int drawableId, int drawableStyle, ParcelableResource updatableResource) {
+            String drawableId, String drawableStyle, ParcelableResource updatableResource) {
         if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
             Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
         }
@@ -149,7 +152,7 @@
 
     // TODO(b/214576716): change this to respect style
     private boolean updateDrawableForSource(
-            int drawableId, int drawableSource, ParcelableResource updatableResource) {
+            String drawableId, String drawableSource, ParcelableResource updatableResource) {
         if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
             Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
         }
@@ -173,11 +176,11 @@
     /**
      * Returns {@code false} if no resources were removed.
      */
-    boolean removeDrawables(@NonNull int[] drawableIds) {
+    boolean removeDrawables(@NonNull String[] drawableIds) {
         synchronized (mLock) {
             boolean removed = false;
             for (int i = 0; i < drawableIds.length; i++) {
-                int drawableId = drawableIds[i];
+                String drawableId = drawableIds[i];
                 removed |= mUpdatedDrawablesForStyle.remove(drawableId) != null
                         || mUpdatedDrawablesForSource.remove(drawableId) != null;
             }
@@ -191,7 +194,7 @@
 
     @Nullable
     ParcelableResource getDrawable(
-            int drawableId, int drawableStyle, int drawableSource) {
+            String drawableId, String drawableStyle, String drawableSource) {
         if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
             Log.w(TAG, "Getting an updated resource for an unknown drawable id " + drawableId);
         }
@@ -231,7 +234,9 @@
             String stringId = strings.get(i).getStringId();
             ParcelableResource resource = strings.get(i).getResource();
 
+            Objects.requireNonNull(stringId, "stringId must be provided.");
             Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+
             updated |= updateString(stringId, resource);
         }
         if (!updated) {
@@ -392,19 +397,19 @@
 
         void writeInner(TypedXmlSerializer out) throws IOException {
             if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) {
-                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry
                         : mUpdatedDrawablesForStyle.entrySet()) {
                     out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
-                    out.attributeInt(
+                    out.attribute(
                             /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
                     out.attributeInt(
                             /* namespace= */ null,
                             ATTR_DRAWABLE_STYLE_SIZE,
                             drawableEntry.getValue().size());
                     int counter = 0;
-                    for (Map.Entry<Integer, ParcelableResource> styleEntry
+                    for (Map.Entry<String, ParcelableResource> styleEntry
                             : drawableEntry.getValue().entrySet()) {
-                        out.attributeInt(
+                        out.attribute(
                                 /* namespace= */ null,
                                 ATTR_DRAWABLE_STYLE + (counter++),
                                 styleEntry.getKey());
@@ -414,19 +419,19 @@
                 }
             }
             if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) {
-                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry
                         : mUpdatedDrawablesForSource.entrySet()) {
                     out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
-                    out.attributeInt(
+                    out.attribute(
                             /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
                     out.attributeInt(
                             /* namespace= */ null,
                             ATTR_DRAWABLE_SOURCE_SIZE,
                             drawableEntry.getValue().size());
                     int counter = 0;
-                    for (Map.Entry<Integer, ParcelableResource> sourceEntry
+                    for (Map.Entry<String, ParcelableResource> sourceEntry
                             : drawableEntry.getValue().entrySet()) {
-                        out.attributeInt(
+                        out.attribute(
                                 /* namespace= */ null,
                                 ATTR_DRAWABLE_SOURCE + (counter++),
                                 sourceEntry.getKey());
@@ -457,7 +462,7 @@
             }
             switch (tag) {
                 case TAG_DRAWABLE_STYLE_ENTRY:
-                    int drawableId = parser.getAttributeInt(
+                    String drawableId = parser.getAttributeValue(
                             /* namespace= */ null, ATTR_DRAWABLE_ID);
                     mUpdatedDrawablesForStyle.put(
                             drawableId,
@@ -465,7 +470,7 @@
                     int size = parser.getAttributeInt(
                             /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE);
                     for (int i = 0; i < size; i++) {
-                        int style = parser.getAttributeInt(
+                        String style = parser.getAttributeValue(
                                 /* namespace= */ null, ATTR_DRAWABLE_STYLE + i);
                         mUpdatedDrawablesForStyle.get(drawableId).put(
                                 style,
@@ -473,13 +478,13 @@
                     }
                     break;
                 case TAG_DRAWABLE_SOURCE_ENTRY:
-                    drawableId = parser.getAttributeInt(
+                    drawableId = parser.getAttributeValue(
                             /* namespace= */ null, ATTR_DRAWABLE_ID);
                     mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
                     size = parser.getAttributeInt(
                             /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE);
                     for (int i = 0; i < size; i++) {
-                        int source = parser.getAttributeInt(
+                        String source = parser.getAttributeValue(
                                 /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i);
                         mUpdatedDrawablesForSource.get(drawableId).put(
                                 source,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 40196db..e0b6273 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -125,6 +125,7 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -199,6 +200,7 @@
 import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
+import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.app.admin.StartInstallingUpdateCallback;
@@ -245,6 +247,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.hardware.usb.UsbManager;
+import android.location.Location;
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.IAudioService;
@@ -260,6 +263,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -325,6 +329,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.LocalePicker;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.net.NetworkUtilsInternal;
@@ -404,6 +409,8 @@
 
     protected static final String LOG_TAG = "DevicePolicyManager";
 
+    private static final String ATTRIBUTION_TAG = "DevicePolicyManagerService";
+
     static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
 
     static final String DEVICE_POLICIES_XML = "device_policies.xml";
@@ -1772,7 +1779,7 @@
      * Instantiates the service.
      */
     public DevicePolicyManagerService(Context context) {
-        this(new Injector(context));
+        this(new Injector(context.createAttributionContext(ATTRIBUTION_TAG)));
     }
 
     @VisibleForTesting
@@ -3357,14 +3364,14 @@
         updatePermissionPolicyCache(userId);
         updateAdminCanGrantSensorsPermissionCache(userId);
 
-        final boolean preferentialNetworkServiceEnabled;
+        final PreferentialNetworkServiceConfig preferentialNetworkServiceConfig;
         synchronized (getLockObject()) {
             ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
-            preferentialNetworkServiceEnabled = owner != null
-                    ? owner.mPreferentialNetworkServiceEnabled
-                             : DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT;
+            preferentialNetworkServiceConfig = owner != null
+                    ? owner.mPreferentialNetworkServiceConfig
+                    : PreferentialNetworkServiceConfig.DEFAULT;
         }
-        updateNetworkPreferenceForUser(userId, preferentialNetworkServiceEnabled);
+        updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfig);
 
         startOwnerService(userId, "start-user");
     }
@@ -3381,7 +3388,7 @@
 
     @Override
     void handleStopUser(int userId) {
-        updateNetworkPreferenceForUser(userId, false);
+        updateNetworkPreferenceForUser(userId, PreferentialNetworkServiceConfig.DEFAULT);
         stopOwnerService(userId, "stop-user");
     }
 
@@ -7209,6 +7216,64 @@
         return getFrpManagementAgentUid() != -1;
     }
 
+    @Override
+    public void sendLostModeLocationUpdate(AndroidFuture<Boolean> future) {
+        if (!mHasFeature) {
+            future.complete(false);
+            return;
+        }
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.SEND_LOST_MODE_LOCATION_UPDATES));
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                    UserHandle.USER_SYSTEM);
+            Preconditions.checkState(admin != null,
+                    "Lost mode location updates can only be sent on an organization-owned device.");
+            mInjector.binderWithCleanCallingIdentity(() -> {
+                final List<String> providers =
+                        mInjector.getLocationManager().getAllProviders().stream()
+                                .filter(mInjector.getLocationManager()::isProviderEnabled)
+                                .collect(Collectors.toList());
+                if (providers.isEmpty()) {
+                    future.complete(false);
+                    return;
+                }
+
+                final CancellationSignal cancellationSignal = new CancellationSignal();
+                List<String> providersWithNullLocation = new ArrayList<String>();
+                for (String provider : providers) {
+                    mInjector.getLocationManager().getCurrentLocation(provider, cancellationSignal,
+                            mContext.getMainExecutor(), location -> {
+                                if (cancellationSignal.isCanceled()) {
+                                    return;
+                                } else if (location != null) {
+                                    sendLostModeLocationUpdate(admin, location);
+                                    cancellationSignal.cancel();
+                                    future.complete(true);
+                                } else {
+                                    // location == null, provider wasn't able to get location, see
+                                    // if there are more providers
+                                    providersWithNullLocation.add(provider);
+                                    if (providers.size() == providersWithNullLocation.size()) {
+                                        future.complete(false);
+                                    }
+                                }
+                            }
+                    );
+                }
+            });
+        }
+    }
+
+    private void sendLostModeLocationUpdate(ActiveAdmin admin, Location location) {
+        final Intent intent = new Intent(
+                DevicePolicyManager.ACTION_LOST_MODE_LOCATION_UPDATE);
+        intent.putExtra(DevicePolicyManager.EXTRA_LOST_MODE_LOCATION, location);
+        intent.setPackage(admin.info.getPackageName());
+        mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
+    }
+
     /**
      * Called by a privileged caller holding {@code BIND_DEVICE_ADMIN} permission to retrieve
      * the remove warning for the given device admin.
@@ -12105,7 +12170,7 @@
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(isProfileOwner(caller),
                 "Caller is not profile owner;"
-                        + " only profile owner may control the preferntial network service");
+                        + " only profile owner may control the preferential network service");
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
                     caller.getUserId());
@@ -12142,6 +12207,47 @@
     }
 
     @Override
+    public void setPreferentialNetworkServiceConfig(
+            PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
+        if (!mHasFeature) {
+            return;
+        }
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwner(caller),
+                "Caller is not profile owner;"
+                        + " only profile owner may control the preferential network service");
+        synchronized (getLockObject()) {
+            final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
+                    caller.getUserId());
+            if (!requiredAdmin.mPreferentialNetworkServiceConfig.equals(
+                    preferentialNetworkServiceConfig)) {
+                requiredAdmin.mPreferentialNetworkServiceConfig = preferentialNetworkServiceConfig;
+                saveSettingsLocked(caller.getUserId());
+            }
+        }
+        updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfig);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED)
+                .setBoolean(preferentialNetworkServiceConfig.isEnabled())
+                .write();
+    }
+
+    @Override
+    public PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() {
+        if (!mHasFeature) {
+            return PreferentialNetworkServiceConfig.DEFAULT;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwner(caller),
+                "Caller is not profile owner");
+        synchronized (getLockObject()) {
+            final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(caller.getUserId());
+            return requiredAdmin.mPreferentialNetworkServiceConfig;
+        }
+    }
+
+    @Override
     public void setLockTaskPackages(ComponentName who, String[] packages)
             throws SecurityException {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -17964,8 +18070,6 @@
         if (!isManagedProfile(userId)) {
             return;
         }
-        int networkPreference = preferentialNetworkServiceEnabled
-                ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
         ProfileNetworkPreference.Builder preferenceBuilder =
                 new ProfileNetworkPreference.Builder();
         if (preferentialNetworkServiceEnabled) {
@@ -17982,6 +18086,40 @@
                         null /* executor */, null /* listener */));
     }
 
+    private void updateNetworkPreferenceForUser(int userId,
+            PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) {
+        if (!isManagedProfile(userId)) {
+            return;
+        }
+        ProfileNetworkPreference.Builder preferenceBuilder =
+                new ProfileNetworkPreference.Builder();
+        if (preferentialNetworkServiceConfig.isEnabled()) {
+            if (preferentialNetworkServiceConfig.isFallbackToDefaultConnectionAllowed()) {
+                preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+            } else {
+                preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+            }
+        } else {
+            preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+        }
+        List<Integer> allowedUids = Arrays.stream(
+                preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect(
+                        Collectors.toList());
+        List<Integer> excludedUids = Arrays.stream(
+                preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect(
+                Collectors.toList());
+        preferenceBuilder.setIncludedUids(allowedUids);
+        preferenceBuilder.setExcludedUids(excludedUids);
+        preferenceBuilder.setPreferenceEnterpriseId(
+                preferentialNetworkServiceConfig.getNetworkId());
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceBuilder.build());
+        mInjector.binderWithCleanCallingIdentity(() ->
+                mInjector.getConnectivityManager().setProfileNetworkPreferences(
+                        UserHandle.of(userId), preferences,
+                        null /* executor */, null /* listener */));
+    }
+
     @Override
     public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
         if (!mHasFeature) {
@@ -18221,13 +18359,13 @@
         mInjector.binderWithCleanCallingIdentity(() -> {
             if (mDeviceManagementResourcesProvider.updateDrawables(drawables)) {
                 sendDrawableUpdatedBroadcast(
-                        drawables.stream().mapToInt(d -> d.getDrawableId()).toArray());
+                        drawables.stream().map(s -> s.getDrawableId()).toArray(String[]::new));
             }
         });
     }
 
     @Override
-    public void resetDrawables(@NonNull int[] drawableIds) {
+    public void resetDrawables(@NonNull String[] drawableIds) {
         Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
                 android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
 
@@ -18241,24 +18379,15 @@
     }
 
     @Override
-    public ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource) {
+    public ParcelableResource getDrawable(
+            String drawableId, String drawableStyle, String drawableSource) {
         return mInjector.binderWithCleanCallingIdentity(() ->
                 mDeviceManagementResourcesProvider.getDrawable(
                         drawableId, drawableStyle, drawableSource));
     }
 
-    private void sendDrawableUpdatedBroadcast(int[] drawableIds) {
-        final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
-        intent.putExtra(EXTRA_RESOURCE_ID, drawableIds);
-        intent.putExtra(EXTRA_RESOURCE_TYPE_DRAWABLE, /* value= */ true);
-        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-
-        List<UserInfo> users = mUserManager.getAliveUsers();
-        for (int i = 0; i < users.size(); i++) {
-            UserHandle user = users.get(i).getUserHandle();
-            mContext.sendBroadcastAsUser(intent, user);
-        }
+    private void sendDrawableUpdatedBroadcast(String[] drawableIds) {
+        sendResourceUpdatedBroadcast(EXTRA_RESOURCE_TYPE_DRAWABLE, drawableIds);
     }
 
     @Override
@@ -18294,9 +18423,13 @@
     }
 
     private void sendStringsUpdatedBroadcast(String[] stringIds) {
+        sendResourceUpdatedBroadcast(EXTRA_RESOURCE_TYPE_STRING, stringIds);
+    }
+
+    private void sendResourceUpdatedBroadcast(String resourceType, String[] resourceIds) {
         final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
-        intent.putExtra(EXTRA_RESOURCE_ID, stringIds);
-        intent.putExtra(EXTRA_RESOURCE_TYPE_STRING, /* value= */ true);
+        intent.putExtra(EXTRA_RESOURCE_ID, resourceIds);
+        intent.putExtra(resourceType, /* value= */ true);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
index 1d4c94d..99b0f25 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
@@ -16,6 +16,8 @@
 
 package com.android.server.selectiontoolbar;
 
+import android.service.selectiontoolbar.DefaultSelectionToolbarRenderService;
+
 import com.android.server.infra.ServiceNameResolver;
 
 import java.io.PrintWriter;
@@ -24,7 +26,7 @@
 
     // TODO: move to SysUi or ExtServices
     private static final String SELECTION_TOOLBAR_SERVICE_NAME =
-            "android/com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService";
+            "android/" + DefaultSelectionToolbarRenderService.class.getName();
 
     @Override
     public String getDefaultServiceName(int userId) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index 57562ef..f266e76 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -31,9 +31,9 @@
     override val creator = ParsedPermissionGroupImpl.CREATOR
 
     override val subclassBaseParams = listOf(
-        ParsedPermissionGroup::getRequestDetailResourceId,
-        ParsedPermissionGroup::getBackgroundRequestDetailResourceId,
-        ParsedPermissionGroup::getBackgroundRequestResourceId,
+        ParsedPermissionGroup::getRequestDetailRes,
+        ParsedPermissionGroup::getBackgroundRequestDetailRes,
+        ParsedPermissionGroup::getBackgroundRequestRes,
         ParsedPermissionGroup::getRequestRes,
         ParsedPermissionGroup::getPriority,
     )
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 037da24..0302d57 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -43,10 +43,9 @@
     override fun mainComponentSubclassExtraParams() = listOf(
         getSetByValue(
             ParsedProvider::getUriPermissionPatterns,
-            ParsedProviderImpl::setUriPermissionPatterns,
+            ParsedProviderImpl::addUriPermissionPattern,
             PatternMatcher("testPattern", PatternMatcher.PATTERN_LITERAL),
-            transformGet = { it?.singleOrNull() },
-            transformSet = { arrayOf(it) },
+            transformGet = { it.singleOrNull() },
             compare = { first, second ->
                 equalBy(
                     first, second,
@@ -57,15 +56,14 @@
         ),
         getSetByValue(
             ParsedProvider::getPathPermissions,
-            ParsedProviderImpl::setPathPermissions,
+            ParsedProviderImpl::addPathPermission,
             PathPermission(
                 "testPermissionPattern",
                 PatternMatcher.PATTERN_LITERAL,
                 "test.READ_PERMISSION",
                 "test.WRITE_PERMISSION"
             ),
-            transformGet = { it?.singleOrNull() },
-            transformSet = { arrayOf(it) },
+            transformGet = { it.singleOrNull() },
             compare = { first, second ->
                 equalBy(
                     first, second,
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 555f4b8..1e0f30e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -521,6 +521,8 @@
         val parsedPackage = parseResult.hideAsParsed() as ParsedPackage
         whenever(mocks.packageParser.parsePackage(
                 or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
+        whenever(mocks.packageParser.parsePackage(
+                or(eq(path), eq(basePath)), anyInt(), anyBoolean(), any())) { parsedPackage }
         return parsedPackage
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index dbd5403..0820a3c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -33,6 +33,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.argThat
@@ -121,7 +122,8 @@
         whenever(rule.mocks().packageParser.parsePackage(
                 argThat { path: File -> path.path.contains("a.data.package") },
                 anyInt(),
-                anyBoolean()))
+                anyBoolean(),
+                any()))
                 .thenThrow(PackageManagerException(
                         PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
         val pm = createPackageManagerService()
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
index 72100e44..e36263e 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -18,17 +18,21 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
 import android.Manifest;
 import android.app.admin.DevicePolicyManager;
+import android.companion.AssociationInfo;
 import android.companion.virtual.IVirtualDeviceActivityListener;
 import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
@@ -41,24 +45,35 @@
 import android.hardware.input.VirtualMouseRelativeEvent;
 import android.hardware.input.VirtualMouseScrollEvent;
 import android.hardware.input.VirtualTouchEvent;
+import android.net.MacAddress;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.WorkSource;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 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.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class VirtualDeviceManagerServiceTest {
 
     private static final String DEVICE_NAME = "device name";
@@ -84,6 +99,11 @@
     private InputManagerInternal mInputManagerInternalMock;
     @Mock
     private IVirtualDeviceActivityListener mActivityListener;
+    @Mock
+    IPowerManager mIPowerManagerMock;
+    @Mock
+    IThermalService mIThermalServiceMock;
+    private PowerManager mPowerManager;
 
     @Before
     public void setUp() {
@@ -102,10 +122,17 @@
         when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
                 mDevicePolicyManagerMock);
 
+        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock,
+                new Handler(TestableLooper.get(this).getLooper()));
+        when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
+
         mInputController = new InputController(new Object(), mNativeWrapperMock);
+        AssociationInfo associationInfo = new AssociationInfo(1, 0, null,
+                MacAddress.BROADCAST_ADDRESS, "", null, true, false, 0, 0);
         mDeviceImpl = new VirtualDeviceImpl(mContext,
-                /* association info */ null, new Binder(), /* uid */ 0, mInputController,
-                (int associationId) -> {}, mPendingTrampolineCallback, mActivityListener,
+                associationInfo, new Binder(), /* uid */ 0, mInputController,
+                (int associationId) -> {
+                }, mPendingTrampolineCallback, mActivityListener,
                 new VirtualDeviceParams.Builder().build());
     }
 
@@ -118,6 +145,72 @@
     }
 
     @Test
+    public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
+        final int displayId = 2;
+        mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+        verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), anyInt());
+        TestableLooper.get(this).processAllMessages();
+        verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), eq(displayId));
+    }
+
+    @Test
+    public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
+            throws RemoteException {
+        final int displayId = 2;
+        mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+        assertThrows(IllegalStateException.class,
+                () -> mDeviceImpl.onVirtualDisplayCreatedLocked(displayId));
+        TestableLooper.get(this).processAllMessages();
+        verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), eq(displayId));
+    }
+
+    @Test
+    public void onVirtualDisplayRemovedLocked_unknownDisplayId_throwsException() {
+        final int unknownDisplayId = 999;
+        assertThrows(IllegalStateException.class,
+                () -> mDeviceImpl.onVirtualDisplayRemovedLocked(unknownDisplayId));
+    }
+
+    @Test
+    public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
+        final int displayId = 2;
+        mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+        ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
+        TestableLooper.get(this).processAllMessages();
+        verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
+                anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), eq(displayId));
+
+        IBinder wakeLock = wakeLockCaptor.getValue();
+        mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
+        verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt());
+    }
+
+    @Test
+    public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
+        final int displayId = 2;
+        mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+        ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
+        TestableLooper.get(this).processAllMessages();
+        verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(),
+                anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), eq(displayId));
+        IBinder wakeLock = wakeLockCaptor.getValue();
+
+        // Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
+        mDeviceImpl.close();
+        verify(mIPowerManagerMock, Mockito.times(1)).releaseWakeLock(eq(wakeLock), anyInt());
+    }
+
+    @Test
     public void createVirtualKeyboard_noDisplay_failsSecurityException() {
         assertThrows(
                 SecurityException.class,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 842a438..c0ad69f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -42,6 +42,7 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 
@@ -94,6 +95,7 @@
 import android.app.admin.DevicePolicyManagerLiteInternal;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.PasswordMetrics;
+import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.WifiSsidPolicy;
 import android.content.BroadcastReceiver;
@@ -4146,6 +4148,164 @@
     }
 
     @Test
+    public void testSetPreferentialNetworkServiceConfig_noProfileOwner() throws Exception {
+        assertExpectException(SecurityException.class, null,
+                () -> dpm.setPreferentialNetworkServiceConfig(
+                        PreferentialNetworkServiceConfig.DEFAULT));
+    }
+
+    @Test
+    public void testIsPreferentialNetworkServiceEnabled_noProfileOwner() throws Exception {
+        assertExpectException(SecurityException.class, null,
+                () -> dpm.isPreferentialNetworkServiceEnabled());
+    }
+
+    @Test
+    public void testSetPreferentialNetworkServiceConfig_invalidConfig() throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        PreferentialNetworkServiceConfig.Builder preferentialNetworkServiceConfigBuilder =
+                new PreferentialNetworkServiceConfig.Builder();
+        assertExpectException(NullPointerException.class, null,
+                () -> preferentialNetworkServiceConfigBuilder.setIncludedUids(null));
+        assertExpectException(NullPointerException.class, null,
+                () -> preferentialNetworkServiceConfigBuilder.setExcludedUids(null));
+        assertExpectException(IllegalArgumentException.class, null,
+                () -> preferentialNetworkServiceConfigBuilder.setNetworkId(6));
+        int[] includedUids = new int[]{1, 2};
+        int[] excludedUids = new int[]{3, 4};
+        preferentialNetworkServiceConfigBuilder.setIncludedUids(includedUids);
+        preferentialNetworkServiceConfigBuilder.setExcludedUids(excludedUids);
+
+        assertExpectException(IllegalStateException.class, null,
+                () -> preferentialNetworkServiceConfigBuilder.build());
+    }
+
+    @Test
+    public void testSetPreferentialNetworkServiceConfig_defaultPreference() throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        dpm.setPreferentialNetworkServiceConfig(PreferentialNetworkServiceConfig.DEFAULT);
+        assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
+        assertThat(dpm.getPreferentialNetworkServiceConfig()
+                .isEnabled()).isFalse();
+
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
+    }
+
+    @Test
+    public void testSetPreferentialNetworkServiceConfig_enterprisePreference() throws Exception {
+        PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+                (new PreferentialNetworkServiceConfig.Builder())
+                        .setEnabled(true)
+                        .setNetworkId(NET_ENTERPRISE_ID_1)
+                        .build();
+
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+        assertThat(dpm.getPreferentialNetworkServiceConfig()
+                .isEnabled()).isTrue();
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+                        .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
+    }
+
+    @Test
+    public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceIncludedUids()
+            throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+                (new PreferentialNetworkServiceConfig.Builder())
+                        .setEnabled(true)
+                        .setNetworkId(NET_ENTERPRISE_ID_1)
+                        .setFallbackToDefaultConnectionAllowed(false)
+                        .setIncludedUids(new int[]{1, 2})
+                        .build();
+        dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+        assertThat(dpm.getPreferentialNetworkServiceConfig()
+                .isEnabled()).isTrue();
+        List<Integer> includedList = new ArrayList<>();
+        includedList.add(1);
+        includedList.add(2);
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
+                        .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+                        .setIncludedUids(includedList)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
+    }
+
+    @Test
+    public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceExcludedUids()
+            throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled =
+                (new PreferentialNetworkServiceConfig.Builder())
+                        .setEnabled(true)
+                        .setNetworkId(NET_ENTERPRISE_ID_1)
+                        .setFallbackToDefaultConnectionAllowed(false)
+                        .setExcludedUids(new int[]{1, 2})
+                        .build();
+
+        dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled);
+        assertThat(dpm.getPreferentialNetworkServiceConfig()
+                .isEnabled()).isTrue();
+        List<Integer> excludedUids = new ArrayList<>();
+        excludedUids.add(1);
+        excludedUids.add(2);
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK)
+                        .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
+                        .setExcludedUids(excludedUids)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.clear();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
+    }
+
+    @Test
     public void testSetSystemSettingFailWithNonWhitelistedSettings() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
@@ -7962,6 +8122,51 @@
                 () -> WifiSsidPolicy.createDenylistPolicy(ssids));
     }
 
+    @Test
+    public void testSendLostModeLocationUpdate_noPermission() {
+        assertThrows(SecurityException.class, () -> dpm.sendLostModeLocationUpdate(
+                getServices().executor, /* empty callback */ result -> {}));
+    }
+
+    @Test
+    public void testSendLostModeLocationUpdate_notOrganizationOwnedDevice() {
+        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        assertThrows(IllegalStateException.class, () -> dpm.sendLostModeLocationUpdate(
+                getServices().executor, /* empty callback */ result -> {}));
+    }
+
+    @Test
+    public void testSendLostModeLocationUpdate_asDeviceOwner() throws Exception {
+        final String TEST_PROVIDER = "network";
+        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        setDeviceOwner();
+        when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
+        when(getServices().locationManager.isProviderEnabled(TEST_PROVIDER)).thenReturn(true);
+
+        dpm.sendLostModeLocationUpdate(getServices().executor, /* empty callback */ result -> {});
+
+        verify(getServices().locationManager, times(1)).getCurrentLocation(
+                eq(TEST_PROVIDER), any(), eq(getServices().executor), any());
+    }
+
+    @Test
+    public void testSendLostModeLocationUpdate_asProfileOwnerOfOrgOwnedDevice() throws Exception {
+        final String TEST_PROVIDER = "network";
+        final int MANAGED_PROFILE_ADMIN_UID =
+                UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+        configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+        when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
+        when(getServices().locationManager.isProviderEnabled(TEST_PROVIDER)).thenReturn(true);
+
+        dpm.sendLostModeLocationUpdate(getServices().executor, /* empty callback */ result -> {});
+
+        verify(getServices().locationManager, times(1)).getCurrentLocation(
+                eq(TEST_PROVIDER), any(), eq(getServices().executor), any());
+    }
+
     private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
         final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
                 userVpnUid, List.of(new AppOpsManager.OpEntry(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 6eb2085..2cf67f8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -47,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * Context used throughout DPMS tests.
@@ -234,6 +235,8 @@
                 return mMockSystemServices.vpnManager;
             case Context.DEVICE_POLICY_SERVICE:
                 return mMockSystemServices.devicePolicyManager;
+            case Context.LOCATION_SERVICE:
+                return mMockSystemServices.locationManager;
         }
         throw new UnsupportedOperationException();
     }
@@ -493,6 +496,11 @@
     }
 
     @Override
+    public Executor getMainExecutor() {
+        return mMockSystemServices.executor;
+    }
+
+    @Override
     public int checkCallingPermission(String permission) {
         return checkPermission(permission);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 597a165..21fb2da 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -47,6 +47,7 @@
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.hardware.usb.UsbManager;
+import android.location.LocationManager;
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
 import android.net.IIpConnectivityMetrics;
@@ -81,6 +82,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -90,6 +92,7 @@
     public final File systemUserDataDir;
     public final EnvironmentForMock environment;
     public final SystemPropertiesForMock systemProperties;
+    public final Executor executor;
     public final UserManager userManager;
     public final UserManagerInternal userManagerInternal;
     public final UsageStatsManagerInternal usageStatsManagerInternal;
@@ -127,6 +130,7 @@
     public final UsbManager usbManager;
     public final VpnManager vpnManager;
     public final DevicePolicyManager devicePolicyManager;
+    public final LocationManager locationManager;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
     public final BuildMock buildMock = new BuildMock();
@@ -138,6 +142,7 @@
 
         environment = mock(EnvironmentForMock.class);
         systemProperties = mock(SystemPropertiesForMock.class);
+        executor = mock(Executor.class);
         userManager = mock(UserManager.class);
         userManagerInternal = mock(UserManagerInternal.class);
         usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
@@ -175,6 +180,7 @@
         usbManager = mock(UsbManager.class);
         vpnManager = mock(VpnManager.class);
         devicePolicyManager = mock(DevicePolicyManager.class);
+        locationManager = mock(LocationManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 54945e4..4caa85c 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -391,4 +391,33 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
         assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
     }
+
+    @Test
+    public void testHysteresisLevels() {
+        int[] ambientBrighteningThresholds = {100, 200};
+        int[] ambientDarkeningThresholds = {400, 500};
+        int[] ambientThresholdLevels = {500};
+        float ambientDarkeningMinChangeThreshold = 3.0f;
+        float ambientBrighteningMinChangeThreshold = 1.5f;
+        HysteresisLevels hysteresisLevels = new HysteresisLevels(ambientBrighteningThresholds,
+                ambientDarkeningThresholds, ambientThresholdLevels,
+                ambientDarkeningMinChangeThreshold, ambientBrighteningMinChangeThreshold);
+
+        // test low, activate minimum change thresholds.
+        assertEquals(1.5f, hysteresisLevels.getBrighteningThreshold(0.0f), EPSILON);
+        assertEquals(0f, hysteresisLevels.getDarkeningThreshold(0.0f), EPSILON);
+        assertEquals(1f, hysteresisLevels.getDarkeningThreshold(4.0f), EPSILON);
+
+        // test max
+        assertEquals(12000f, hysteresisLevels.getBrighteningThreshold(10000.0f), EPSILON);
+        assertEquals(5000f, hysteresisLevels.getDarkeningThreshold(10000.0f), EPSILON);
+
+        // test just below threshold
+        assertEquals(548.9f, hysteresisLevels.getBrighteningThreshold(499f), EPSILON);
+        assertEquals(299.4f, hysteresisLevels.getDarkeningThreshold(499f), EPSILON);
+
+        // test at (considered above) threshold
+        assertEquals(600f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
+        assertEquals(250f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 81c9871..e80721a 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -51,6 +51,7 @@
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
 import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -71,7 +72,6 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
-import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -95,6 +95,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -495,8 +496,14 @@
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
-        // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
-        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+        // Catch UsageCallback during systemReady(). Simulate NetworkStatsService triggered
+        // stats updated callback to signal its readiness.
+        final ArgumentCaptor<NetworkStatsManager.UsageCallback> usageObserver =
+                ArgumentCaptor.forClass(NetworkStatsManager.UsageCallback.class);
+        verify(mStatsManager, times(2))
+                .registerUsageCallback(any(), anyLong(), any(), usageObserver.capture());
+        usageObserver.getValue().onThresholdReached(
+                new NetworkTemplate.Builder(MATCH_MOBILE).build());
 
         NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
         mDefaultWarningBytes = defaultPolicy.warningBytes;
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 d8ecf20..31bdec1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -65,7 +65,7 @@
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.CompatibilityPermissionInfo;
-import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedActivityImpl;
 import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -673,9 +673,9 @@
         assertArrayEquals(a.getSplitFlags(), b.getSplitFlags());
 
         PackageInfo aInfo = PackageInfoUtils.generate(a, new int[]{}, 0, 0, 0,
-                Collections.emptySet(), PackageUserState.DEFAULT, 0, mockPkgSetting(a));
+                Collections.emptySet(), PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(a));
         PackageInfo bInfo = PackageInfoUtils.generate(b, new int[]{}, 0, 0, 0,
-                Collections.emptySet(), PackageUserState.DEFAULT, 0, mockPkgSetting(b));
+                Collections.emptySet(), PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(b));
         assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
 
         assertEquals(ArrayUtils.size(a.getPermissions()), ArrayUtils.size(b.getPermissions()));
@@ -831,9 +831,9 @@
 
         // Validity check for ServiceInfo.
         ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0,
-                PackageUserState.DEFAULT, 0, mockPkgSetting(aPkg));
+                PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(aPkg));
         ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0,
-                PackageUserState.DEFAULT, 0, mockPkgSetting(bPkg));
+                PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(bPkg));
         assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
         assertEquals(a.getName(), b.getName());
     }
@@ -858,9 +858,9 @@
 
         // Validity check for ActivityInfo.
         ActivityInfo aInfo = PackageInfoUtils.generateActivityInfo(aPkg, a, 0,
-                PackageUserState.DEFAULT, 0, mockPkgSetting(aPkg));
+                PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(aPkg));
         ActivityInfo bInfo = PackageInfoUtils.generateActivityInfo(bPkg, b, 0,
-                PackageUserState.DEFAULT, 0, mockPkgSetting(bPkg));
+                PackageUserStateInternal.DEFAULT, 0, mockPkgSetting(bPkg));
         assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
         assertEquals(a.getName(), b.getName());
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 7ff8eec..4a24bbd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -574,7 +574,7 @@
         assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
 
         final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
-                pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
+                pkgSetting.getPkg(), 0, pkgSetting.getUserStateOrDefault(0), 0, pkgSetting);
         assertBasicApplicationInfo(scanResult, applicationInfo);
     }
 
@@ -612,7 +612,7 @@
     private static void assertAbiAndPathssDerived(ScanResult scanResult) {
         PackageSetting pkgSetting = scanResult.mPkgSetting;
         final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
-                pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
+                pkgSetting.getPkg(), 0, pkgSetting.getUserStateOrDefault(0), 0, pkgSetting);
         assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
         assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary"));
 
@@ -626,7 +626,7 @@
     private static void assertPathsNotDerived(ScanResult scanResult) {
         PackageSetting pkgSetting = scanResult.mPkgSetting;
         final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
-                pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
+                pkgSetting.getPkg(), 0, pkgSetting.getUserStateOrDefault(0), 0, pkgSetting);
         assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
         assertThat(pkgSetting.getLegacyNativeLibraryPath(), is("getRootDir"));
         assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7e5fe04..401cd7f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -309,7 +309,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_restrictedReturnsError() throws Exception {
+    public void testRemoveUserWhenPossible_restrictedReturnsError() throws Exception {
         final int currentUser = ActivityManager.getCurrentUser();
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
@@ -328,7 +328,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_evenWhenRestricted() throws Exception {
+    public void testRemoveUserWhenPossible_evenWhenRestricted() throws Exception {
         final int currentUser = ActivityManager.getCurrentUser();
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
@@ -351,7 +351,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
+    public void testRemoveUserWhenPossible_systemUserReturnsError() throws Exception {
         assertThat(mUserManager.removeUserWhenPossible(UserHandle.SYSTEM,
                 /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
 
@@ -360,7 +360,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
+    public void testRemoveUserWhenPossible_invalidUserReturnsError() throws Exception {
         assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
         assertThat(mUserManager.removeUserWhenPossible(UserHandle.of(Integer.MAX_VALUE),
                 /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
@@ -368,7 +368,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_currentUserSetEphemeral() throws Exception {
+    public void testRemoveUserWhenPossible_currentUserSetEphemeral() throws Exception {
         final int startUser = ActivityManager.getCurrentUser();
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         // Switch to the user just created.
@@ -392,7 +392,7 @@
 
     @MediumTest
     @Test
-    public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
+    public void testRemoveUserWhenPossible_nonCurrentUserRemoved() throws Exception {
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         synchronized (mUserRemoveLock) {
             assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 774e5b9..30ad1f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -40,6 +40,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.Process.NOBODY_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsState.ITYPE_IME;
@@ -83,6 +84,7 @@
 import static com.android.server.wm.ActivityRecord.State.STARTED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
@@ -3305,6 +3307,29 @@
                 CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
     }
 
+    @Test // b/162542125
+    public void testInputDispatchTimeout() throws RemoteException {
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowProcessController wpc = activity.app;
+        spyOn(wpc);
+        doReturn(true).when(wpc).isInstrumenting();
+        final ActivityRecord instrumentingActivity = createActivityOnDisplay(
+                true /* defaultDisplay */, wpc);
+        assertEquals(INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS,
+                instrumentingActivity.mInputDispatchingTimeoutMillis);
+
+        doReturn(false).when(wpc).isInstrumenting();
+        final ActivityRecord nonInstrumentingActivity = createActivityOnDisplay(
+                true /* defaultDisplay */, wpc);
+        assertEquals(DEFAULT_DISPATCHING_TIMEOUT_MILLIS,
+                nonInstrumentingActivity.mInputDispatchingTimeoutMillis);
+
+        final ActivityRecord noProcActivity = createActivityOnDisplay(true /* defaultDisplay */,
+                null);
+        assertEquals(DEFAULT_DISPATCHING_TIMEOUT_MILLIS,
+                noProcActivity.mInputDispatchingTimeoutMillis);
+    }
+
     private ICompatCameraControlCallback getCompatCameraControlCallback() {
         return new ICompatCameraControlCallback.Stub() {
             @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8d58ec0..ea03250 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1340,7 +1340,7 @@
         displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
         displayContent.setRotationAnimation(rotationAnim);
         // The fade rotation animation also starts to hide some non-app windows.
-        assertNotNull(displayContent.getFadeRotationAnimationController());
+        assertNotNull(displayContent.getAsyncRotationController());
         assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
 
         for (WindowState w : windows) {
@@ -1354,7 +1354,7 @@
         // the animation controller should be cleared.
         statusBar.setOrientationChanging(false);
         navBar.setOrientationChanging(false);
-        assertNull(displayContent.getFadeRotationAnimationController());
+        assertNull(displayContent.getAsyncRotationController());
     }
 
     @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
@@ -1392,7 +1392,7 @@
                 ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
                 false /* forceUpdate */));
 
-        assertNotNull(mDisplayContent.getFadeRotationAnimationController());
+        assertNotNull(mDisplayContent.getAsyncRotationController());
         assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
         assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
         // Notification shade may have its own view animation in real case so do not fade out it.
@@ -1482,7 +1482,7 @@
         assertFalse(app.hasFixedRotationTransform());
         assertFalse(app2.hasFixedRotationTransform());
         assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
-        assertNull(mDisplayContent.getFadeRotationAnimationController());
+        assertNull(mDisplayContent.getAsyncRotationController());
     }
 
     @Test
@@ -1598,6 +1598,30 @@
         assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
     }
 
+    /**
+     * Creates different types of displays, verifies that minimal task size doesn't change
+     * with density of display.
+     */
+    @Test
+    public void testCalculatesDisplaySpecificMinTaskSizes() {
+        DisplayContent defaultTestDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 2000).build();
+        final int defaultMinTaskSize = defaultTestDisplay.mMinSizeOfResizeableTaskDp;
+        DisplayContent firstDisplay = new TestDisplayContent.Builder(mAtm, 1000, 2000)
+                .setDensityDpi(300)
+                .updateDisplayMetrics()
+                .setDefaultMinTaskSizeDp(defaultMinTaskSize + 10)
+                .build();
+        assertEquals(defaultMinTaskSize + 10, firstDisplay.mMinSizeOfResizeableTaskDp);
+
+        DisplayContent secondDisplay = new TestDisplayContent.Builder(mAtm, 200, 200)
+                .setDensityDpi(320)
+                .updateDisplayMetrics()
+                .setDefaultMinTaskSizeDp(defaultMinTaskSize + 20)
+                .build();
+        assertEquals(defaultMinTaskSize + 20, secondDisplay.mMinSizeOfResizeableTaskDp);
+    }
+
     @Test
     public void testRecentsNotRotatingWithFixedRotation() {
         unblockDisplayRotation(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 6342183..25cff61c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
@@ -546,6 +547,80 @@
                 SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
     }
 
+    @Test
+    public void testReturnsSensorRotation_180degrees_allRotationsAllowed()
+            throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+        enableOrientationSensor();
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+        assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
+    }
+
+    @Test
+    public void testReturnLastRotation_sensor180_allRotationsNotAllowed()
+            throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(false);
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+        enableOrientationSensor();
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
+    }
+
+    @Test
+    public void testAllowRotationsIsCached()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+        enableOrientationSensor();
+
+        // Rotate once to read the resource
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(true);
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+        mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
+
+        // Change resource to disallow all rotations.
+        // Rotate again and 180 degrees rotation should still be returned even if "disallowed".
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(false);
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+        assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
+    }
+
+    @Test
+    public void testResetAllowRotations()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+        enableOrientationSensor();
+
+        // Rotate once to read the resource
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(true);
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+        mTarget.rotationForOrientation(SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0);
+
+        // Change resource to disallow all rotations.
+        // Reset "allowAllRotations".
+        // Rotate again and 180 degrees rotation should not be allowed anymore.
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations))
+                .thenReturn(false);
+        mTarget.resetAllowAllRotations();
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_SENSOR, Surface.ROTATION_0));
+    }
+
     // =================================
     // Tests for Policy based Rotation
     // =================================
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 365e749..093be82 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -20,10 +20,10 @@
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -260,6 +260,17 @@
     }
 
     @Test
+    public void testResetAllowAllRotations() {
+        final DisplayRotation displayRotation = mock(DisplayRotation.class);
+        spyOn(mPrimaryDisplay);
+        doReturn(displayRotation).when(mPrimaryDisplay).getDisplayRotation();
+
+        mDisplayWindowSettings.applyRotationSettingsToDisplayLocked(mPrimaryDisplay);
+
+        verify(displayRotation).resetAllowAllRotations();
+    }
+
+    @Test
     public void testDefaultToFreeUserRotation() {
         mDisplayWindowSettings.applySettingsToDisplayLocked(mSecondaryDisplay);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
similarity index 89%
rename from services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index d64bf12..670eb75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
@@ -48,8 +47,7 @@
 import android.view.WindowManagerGlobal;
 import android.window.WindowTokenClient;
 
-import com.android.server.inputmethod.InputMethodManagerService;
-import com.android.server.inputmethod.InputMethodMenuController;
+import com.android.server.inputmethod.InputMethodDialogWindowContext;
 
 import org.junit.After;
 import org.junit.Before;
@@ -61,13 +59,13 @@
 //  scenario there.
 /**
  * Build/Install/Run:
- *  atest WmTests:InputMethodMenuControllerTest
+ *  atest WmTests:InputMethodDialogWindowContextTest
  */
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class InputMethodMenuControllerTest extends WindowTestsBase {
+public class InputMethodDialogWindowContextTest extends WindowTestsBase {
 
-    private InputMethodMenuController mController;
+    private InputMethodDialogWindowContext mWindowContext;
     private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
 
     private IWindowManager mIWindowManager;
@@ -80,7 +78,7 @@
                 new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
         Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
 
-        mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
+        mWindowContext = new InputMethodDialogWindowContext();
         mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
                 .Builder(mAtm, 1000, 1000).build();
         mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
@@ -119,21 +117,21 @@
 
     @Test
     public void testGetSettingsContext() {
-        final Context contextOnDefaultDisplay = mController.getSettingsContext(DEFAULT_DISPLAY);
+        final Context contextOnDefaultDisplay = mWindowContext.get(DEFAULT_DISPLAY);
 
         assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
 
         // Obtain the context again and check if the window metrics match the IME container bounds
         // of the secondary display.
-        final Context contextOnSecondaryDisplay = mController.getSettingsContext(
-                mSecondaryDisplay.getDisplayId());
+        final Context contextOnSecondaryDisplay =
+                mWindowContext.get(mSecondaryDisplay.getDisplayId());
 
         assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
     }
 
     @Test
     public void testGetSettingsContextOnDualDisplayContent() {
-        final Context context = mController.getSettingsContext(mSecondaryDisplay.getDisplayId());
+        final Context context = mWindowContext.get(mSecondaryDisplay.getDisplayId());
         final WindowTokenClient tokenClient = (WindowTokenClient) context.getWindowContextToken();
         assertNotNull(tokenClient);
         spyOn(tokenClient);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 632a59d..87f76fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -621,9 +621,9 @@
     @Test
     public void testNotAttachNavigationBar_controlledByFadeRotationAnimation() {
         setupForShouldAttachNavBarDuringTransition();
-        FadeRotationAnimationController mockController =
-                mock(FadeRotationAnimationController.class);
-        doReturn(mockController).when(mDefaultDisplay).getFadeRotationAnimationController();
+        AsyncRotationController mockController =
+                mock(AsyncRotationController.class);
+        doReturn(mockController).when(mDefaultDisplay).getAsyncRotationController();
         final ActivityRecord homeActivity = createHomeActivity();
         initializeRecentsAnimationController(mController, homeActivity);
         assertFalse(mController.isNavigationBarAttachedToApp());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 575e082..a4851ad5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -665,9 +665,9 @@
 
     @Test
     public void testNonAppTarget_notSendNavBar_controlledByFadeRotation() throws Exception {
-        final FadeRotationAnimationController mockController =
-                mock(FadeRotationAnimationController.class);
-        doReturn(mockController).when(mDisplayContent).getFadeRotationAnimationController();
+        final AsyncRotationController mockController =
+                mock(AsyncRotationController.class);
+        doReturn(mockController).when(mDisplayContent).getAsyncRotationController();
         final int transit = TRANSIT_OLD_TASK_OPEN;
         setupForNonAppTargetNavBar(transit, true);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 8b0716c..1e64e46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -29,18 +29,26 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
+import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
 import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 class TestDisplayContent extends DisplayContent {
 
     public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
@@ -85,6 +93,11 @@
         private boolean mSystemDecorations = false;
         private int mStatusBarHeight = 0;
         private SettingsEntry mOverrideSettings;
+        private DisplayMetrics mDisplayMetrics;
+        @Mock
+        Context mMockContext;
+        @Mock
+        Resources mResources;
 
         Builder(ActivityTaskManagerService service, int width, int height) {
             mService = service;
@@ -97,6 +110,8 @@
             // Set unique ID so physical display overrides are not inheritted from
             // DisplayWindowSettings.
             mInfo.uniqueId = generateUniqueId();
+            mDisplayMetrics = new DisplayMetrics();
+            updateDisplayMetrics();
         }
         Builder(ActivityTaskManagerService service, DisplayInfo info) {
             mService = service;
@@ -153,6 +168,20 @@
             mInfo.logicalDensityDpi = dpi;
             return this;
         }
+        Builder updateDisplayMetrics() {
+            mInfo.getAppMetrics(mDisplayMetrics);
+            return this;
+        }
+        Builder setDefaultMinTaskSizeDp(int valueDp) {
+            MockitoAnnotations.initMocks(this);
+            doReturn(mMockContext).when(mService.mContext).createConfigurationContext(any());
+            doReturn(mResources).when(mMockContext).getResources();
+            doReturn(valueDp * mDisplayMetrics.density)
+                    .when(mResources)
+                    .getDimension(
+                        com.android.internal.R.dimen.default_minimal_size_resizable_task);
+            return this;
+        }
         TestDisplayContent createInternal(Display display) {
             return new TestDisplayContent(mService.mRootWindowContainer, display);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ae3ce04..fd523f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -50,6 +50,7 @@
 import android.util.ArraySet;
 import android.view.SurfaceControl;
 import android.view.TransactionCommittedListener;
+import android.window.IDisplayAreaOrganizer;
 import android.window.ITaskOrganizer;
 import android.window.ITransitionPlayer;
 import android.window.TransitionInfo;
@@ -60,6 +61,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -106,7 +109,7 @@
         // Check basic both tasks participating
         participants.add(oldTask);
         participants.add(newTask);
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
         assertEquals(2, info.getChanges().size());
         assertEquals(transit, info.getType());
@@ -171,7 +174,7 @@
         participants.add(oldTask);
         participants.add(opening);
         participants.add(opening2);
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
         assertEquals(2, info.getChanges().size());
         assertEquals(transit, info.getType());
@@ -217,15 +220,14 @@
         // Check promotion to DisplayArea
         participants.add(showing);
         participants.add(showing2);
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
         assertEquals(1, info.getChanges().size());
         assertEquals(transit, info.getType());
         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
 
-        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
         // Check that organized tasks get reported even if not top
-        showTask.mTaskOrganizer = mockOrg;
+        makeTaskOrganized(showTask);
         targets = Transition.calculateTargets(participants, changes);
         info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
         assertEquals(2, info.getChanges().size());
@@ -255,7 +257,7 @@
         opening.mVisibleRequested = true;
         closing.mVisibleRequested = false;
 
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(
                 transition.mParticipants, transition.mChanges);
         TransitionInfo info = Transition.calculateTransitionInfo(
                 0, 0, targets, transition.mChanges);
@@ -292,7 +294,7 @@
             tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0;
         }
 
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(
                 transition.mParticipants, transition.mChanges);
         TransitionInfo info = Transition.calculateTransitionInfo(
                 0, 0, targets, transition.mChanges);
@@ -341,7 +343,7 @@
             tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0;
         }
 
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(
                 transition.mParticipants, transition.mChanges);
         TransitionInfo info = Transition.calculateTransitionInfo(
                 0, 0, targets, transition.mChanges);
@@ -363,13 +365,7 @@
                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
         // Make DA organized so we can check that they don't get included.
         WindowContainer parent = wallpaperWindowToken.getParent();
-        while (parent != null && parent != mDisplayContent) {
-            if (parent.asDisplayArea() != null) {
-                parent.asDisplayArea().setOrganizer(
-                        mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
-            }
-            parent = parent.getParent();
-        }
+        makeDisplayAreaOrganized(parent, mDisplayContent);
         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
                 "wallpaperWindow");
         wallpaperWindowToken.setVisibleRequested(false);
@@ -381,7 +377,7 @@
         mDisplayContent.getWindowConfiguration().setRotation(
                 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
 
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+        ArrayList<WindowContainer> targets = Transition.calculateTargets(
                 transition.mParticipants, transition.mChanges);
         TransitionInfo info = Transition.calculateTransitionInfo(
                 0, 0, targets, transition.mChanges);
@@ -394,11 +390,37 @@
     }
 
     @Test
+    public void testOpenActivityInTheSameTaskWithDisplayChange() {
+        final ActivityRecord closing = createActivityRecord(mDisplayContent);
+        closing.mVisibleRequested = true;
+        final Task task = closing.getTask();
+        makeTaskOrganized(task);
+        final ActivityRecord opening = createActivityRecord(task);
+        opening.mVisibleRequested = false;
+        makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent);
+        final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent };
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
+        for (WindowContainer<?> wc : wcs) {
+            transition.collect(wc);
+        }
+        closing.mVisibleRequested = false;
+        opening.mVisibleRequested = true;
+        final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1;
+        for (WindowContainer<?> wc : wcs) {
+            wc.getWindowConfiguration().setRotation(newRotation);
+        }
+
+        final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+                transition.mParticipants, transition.mChanges);
+        // Especially the activities must be in the targets.
+        assertTrue(targets.containsAll(Arrays.asList(wcs)));
+    }
+
+    @Test
     public void testIndependent() {
         final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
-        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
 
         final Task openTask = createTask(mDisplayContent);
         final Task openInOpenTask = createTaskInRootTask(openTask, 0);
@@ -410,10 +432,8 @@
         final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
         final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
         // set organizer for everything so that they all get added to transition info
-        for (Task t : new Task[]{
-                openTask, openInOpenTask, changeTask, changeInChangeTask, openInChangeTask}) {
-            t.mTaskOrganizer = mockOrg;
-        }
+        makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask,
+                openInChangeTask);
 
         // Start states.
         changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
@@ -440,7 +460,8 @@
         participants.add(openInChange);
         // Explicitly add changeTask (to test independence with parents)
         participants.add(changeTask);
-        ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+        final ArrayList<WindowContainer> targets =
+                Transition.calculateTargets(participants, changes);
         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
         // Root changes should always be considered independent
         assertTrue(isIndependent(
@@ -494,9 +515,9 @@
         mDisplayContent.setLastHasContent();
         mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
                 null /* displayChange */);
-        final FadeRotationAnimationController fadeController =
-                mDisplayContent.getFadeRotationAnimationController();
-        assertNotNull(fadeController);
+        final AsyncRotationController asyncRotationController =
+                mDisplayContent.getAsyncRotationController();
+        assertNotNull(asyncRotationController);
         for (WindowState w : windows) {
             w.setOrientationChanging(true);
         }
@@ -509,7 +530,7 @@
         // Status bar finishes drawing before the start transaction. Its fade-in animation will be
         // executed until the transaction is committed, so it is still in target tokens.
         statusBar.setOrientationChanging(false);
-        assertTrue(fadeController.isTargetToken(statusBar.mToken));
+        assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));
 
         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
         final ArgumentCaptor<TransactionCommittedListener> listenerCaptor =
@@ -519,13 +540,13 @@
         verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
         // The transaction is committed, so fade-in animation for status bar is consumed.
         listenerCaptor.getValue().onTransactionCommitted();
-        assertFalse(fadeController.isTargetToken(statusBar.mToken));
+        assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
 
         // Status bar finishes drawing after the start transaction, so its fade-in animation can
         // execute directly.
         navBar.setOrientationChanging(false);
-        assertFalse(fadeController.isTargetToken(navBar.mToken));
-        assertNull(mDisplayContent.getFadeRotationAnimationController());
+        assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
+        assertNull(mDisplayContent.getAsyncRotationController());
     }
 
     @Test
@@ -543,10 +564,10 @@
         mDisplayContent.setLastHasContent();
         mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
         transition.setKnownConfigChanges(mDisplayContent, anyChanges);
-        final FadeRotationAnimationController fadeController =
-                mDisplayContent.getFadeRotationAnimationController();
-        assertNotNull(fadeController);
-        assertTrue(fadeController.shouldFreezeInsetsPosition(statusBar));
+        final AsyncRotationController asyncRotationController =
+                mDisplayContent.getAsyncRotationController();
+        assertNotNull(asyncRotationController);
+        assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
 
         statusBar.setOrientationChanging(true);
         player.startTransition();
@@ -572,7 +593,7 @@
         // The controller should capture the draw transaction and merge it when preparing to run
         // fade-in animation.
         verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
-        assertNull(mDisplayContent.getFadeRotationAnimationController());
+        assertNull(mDisplayContent.getAsyncRotationController());
     }
 
     @Test
@@ -581,17 +602,15 @@
         final TransitionController controller = new TransitionController(mAtm, snapshotController);
         final ITransitionPlayer player = new ITransitionPlayer.Default();
         controller.registerTransitionPlayer(player, null /* appThread */);
-        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
 
         // Start out with task2 visible and set up a transition that closes task2 and opens task1
         final Task task1 = createTask(mDisplayContent);
-        task1.mTaskOrganizer = mockOrg;
         final ActivityRecord activity1 = createActivityRecord(task1);
         activity1.mVisibleRequested = false;
         activity1.setVisible(false);
         final Task task2 = createTask(mDisplayContent);
-        task2.mTaskOrganizer = mockOrg;
+        makeTaskOrganized(task1, task2);
         final ActivityRecord activity2 = createActivityRecord(task1);
         activity2.mVisibleRequested = true;
         activity2.setVisible(true);
@@ -647,17 +666,15 @@
         final TransitionController controller = new TransitionController(mAtm, snapshotController);
         final ITransitionPlayer player = new ITransitionPlayer.Default();
         controller.registerTransitionPlayer(player, null /* appThread */);
-        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
 
         // Start out with task2 visible and set up a transition that closes task2 and opens task1
         final Task task1 = createTask(mDisplayContent);
-        task1.mTaskOrganizer = mockOrg;
         final ActivityRecord activity1 = createActivityRecord(task1);
         activity1.mVisibleRequested = false;
         activity1.setVisible(false);
         final Task task2 = createTask(mDisplayContent);
-        task2.mTaskOrganizer = mockOrg;
+        makeTaskOrganized(task1, task2);
         final ActivityRecord activity2 = createActivityRecord(task2);
         activity2.mVisibleRequested = true;
         activity2.setVisible(true);
@@ -706,6 +723,24 @@
         verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
     }
 
+    private static void makeTaskOrganized(Task... tasks) {
+        final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
+        for (Task t : tasks) {
+            t.mTaskOrganizer = organizer;
+        }
+    }
+
+    private static void makeDisplayAreaOrganized(WindowContainer<?> from,
+            WindowContainer<?> end) {
+        final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
+        while (from != null && from != end) {
+            if (from.asDisplayArea() != null) {
+                from.asDisplayArea().mOrganizer = organizer;
+            }
+            from = from.getParent();
+        }
+    }
+
     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
             WindowContainer top) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index caaf4e4..1f68608 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -116,6 +117,7 @@
 
         WindowManager.LayoutParams attrs = wallpaperWindow.getAttrs();
         Rect bounds = dc.getBounds();
+        int displayWidth = dc.getBounds().width();
         int displayHeight = dc.getBounds().height();
 
         // Use a wallpaper with a different ratio than the display
@@ -123,20 +125,18 @@
         int wallpaperHeight = (int) (bounds.height() * 1.10);
 
         // Simulate what would be done on the client's side
-        attrs.width = wallpaperWidth;
-        attrs.height = wallpaperHeight;
-        attrs.flags |= FLAG_LAYOUT_NO_LIMITS;
+        final float layoutScale = Math.max(
+                displayWidth / (float) wallpaperWidth, displayHeight / (float) wallpaperHeight);
+        attrs.width = (int) (wallpaperWidth * layoutScale + .5f);
+        attrs.height = (int) (wallpaperHeight * layoutScale + .5f);
+        attrs.flags |= FLAG_LAYOUT_NO_LIMITS | FLAG_SCALED;
         attrs.gravity = Gravity.TOP | Gravity.LEFT;
         wallpaperWindow.getWindowFrames().mParentFrame.set(dc.getBounds());
 
-        // Calling layoutWindowLw a first time, so adjustWindowParams gets the correct data
-        dc.getDisplayPolicy().layoutWindowLw(wallpaperWindow, null, dc.mDisplayFrames);
-
-        wallpaperWindowToken.adjustWindowParams(wallpaperWindow, attrs);
         dc.getDisplayPolicy().layoutWindowLw(wallpaperWindow, null, dc.mDisplayFrames);
 
         assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getConfiguration().orientation);
-        int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight));
+        int expectedWidth = (int) (wallpaperWidth * layoutScale + .5f);
 
         // Check that the wallpaper is correctly scaled
         assertEquals(expectedWidth, wallpaperWindow.getFrame().width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 62c1067..4095728 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -883,7 +883,8 @@
 
     /** Sets the default minimum task size to 1 so that tests can use small task sizes */
     public void removeGlobalMinSizeRestriction() {
-        mAtm.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
+        mAtm.mRootWindowContainer.forAllDisplays(
+                displayContent -> displayContent.mMinSizeOfResizeableTaskDp = 1);
     }
 
     /** Mocks the behavior of taking a snapshot. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
deleted file mode 100644
index 926153d..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.utils;
-
-import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Tests for {@link DisplayRotationUtil}
- *
- * Build/Install/Run:
- *  atest WmTests:DisplayRotationUtilTest
- */
-@SmallTest
-@Presubmit
-public class DisplayRotationUtilTest {
-    private static final Rect ZERO_RECT = new Rect();
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot0() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot90() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
-                equalTo(BOUNDS_POSITION_RIGHT));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot180() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
-                equalTo(BOUNDS_POSITION_LEFT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
-                equalTo(BOUNDS_POSITION_TOP));
-    }
-
-    @Test
-    public void testGetBoundIndexFromRotation_rot270() {
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
-                equalTo(BOUNDS_POSITION_TOP));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
-                equalTo(BOUNDS_POSITION_RIGHT));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
-                equalTo(BOUNDS_POSITION_BOTTOM));
-        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
-                equalTo(BOUNDS_POSITION_LEFT));
-
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot0() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
-                equalTo(bounds));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot90() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
-                equalTo(new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT}));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot180() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
-                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300)}));
-    }
-
-    @Test
-    public void testGetRotatedBounds_top_rot270() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
-                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT}));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot0() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
-                equalTo(bounds));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot90() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
-                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300)}));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot180() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
-                equalTo(new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT}));
-    }
-
-    @Test
-    public void testGetRotatedBounds_left_rot270() {
-        DisplayRotationUtil util = new DisplayRotationUtil();
-        Rect[] bounds = new Rect[] {new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT};
-        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
-                equalTo(new Rect[] {ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT}));
-    }
-}