Merge "Exclude WindowState from transition targets"
diff --git a/Android.bp b/Android.bp
index 8bc5d7a..838f304 100644
--- a/Android.bp
+++ b/Android.bp
@@ -177,6 +177,7 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
+                "framework-auxiliary.impl",
                 "framework-supplementalapi.impl",
             ],
         },
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c50d446..b5acfb2 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -55,25 +55,9 @@
     "android-support-multidex-instrumentation",
 ]
 
+// These defaults enable doc-stub generation, api lint database generation and sdk value generation.
 stubs_defaults {
     name: "android-non-updatable-doc-stubs-defaults",
-    defaults: [
-        "android-non-updatable-stubs-defaults",
-        "module-classpath-stubs-defaults",
-    ],
-    srcs: [
-        // No longer part of the stubs, but are included in the docs.
-        ":android-test-base-sources",
-        ":android-test-mock-sources",
-        ":android-test-runner-sources",
-    ],
-    libs: framework_docs_only_libs,
-    create_doc_stubs: true,
-    write_sdk_values: true,
-}
-
-stubs_defaults {
-    name: "framework-doc-stubs-default",
     defaults: ["android-non-updatable-stubs-defaults"],
     srcs: [
         // No longer part of the stubs, but are included in the docs.
@@ -83,24 +67,20 @@
     ],
     libs: framework_docs_only_libs,
     create_doc_stubs: true,
-    api_levels_annotations_enabled: true,
-    api_levels_annotations_dirs: [
-        "sdk-dir",
-        "api-versions-jars-dir",
-    ],
     write_sdk_values: true,
 }
 
 // Defaults module for doc-stubs targets that use module source code as input.
 stubs_defaults {
     name: "framework-doc-stubs-sources-default",
-    defaults: ["framework-doc-stubs-default"],
+    defaults: ["android-non-updatable-doc-stubs-defaults"],
     srcs: [
         ":art.module.public.api{.public.stubs.source}",
         ":conscrypt.module.public.api{.public.stubs.source}",
         ":i18n.module.public.api{.public.stubs.source}",
 
         ":framework-appsearch-sources",
+        ":framework-auxiliary-sources",
         ":framework-connectivity-sources",
         ":framework-bluetooth-sources",
         ":framework-connectivity-tiramisu-updatable-sources",
@@ -123,13 +103,19 @@
 
 droidstubs {
     name: "android-non-updatable-doc-stubs",
-    defaults: ["android-non-updatable-doc-stubs-defaults"],
+    defaults: [
+        "android-non-updatable-doc-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args,
 }
 
 droidstubs {
     name: "android-non-updatable-doc-stubs-system",
-    defaults: ["android-non-updatable-doc-stubs-defaults"],
+    defaults: [
+        "android-non-updatable-doc-stubs-defaults",
+        "module-classpath-stubs-defaults",
+    ],
     args: metalava_framework_docs_args +
         " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
 }
@@ -139,14 +125,24 @@
     defaults: ["framework-doc-stubs-sources-default"],
     args: metalava_framework_docs_args +
         " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
     api_levels_sdk_type: "system",
 }
 
 droidstubs {
     name: "framework-doc-stubs",
-    defaults: ["framework-doc-stubs-default"],
+    defaults: ["android-non-updatable-doc-stubs-defaults"],
     srcs: [":all-modules-public-stubs-source"],
     args: metalava_framework_docs_args,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
     aidl: {
         local_include_dirs: [
             "apex/media/aidl/stable",
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 6dd79e8..afad29c 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -259,10 +259,9 @@
      */
     public static final int REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207;
     /**
-     * Broadcast {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}.
-     * @hide
+     * Broadcast {@link android.safetycenter.SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES}.
      */
-    public static final int REASON_ACTION_REFRESH_SAFETY_SOURCES = 208;
+    public static final int REASON_REFRESH_SAFETY_SOURCES = 208;
 
     /* Reason code range 300-399 are reserved for other internal reasons */
     /**
@@ -419,7 +418,7 @@
             REASON_TIME_CHANGED,
             REASON_LOCALE_CHANGED,
             REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
-            REASON_ACTION_REFRESH_SAFETY_SOURCES,
+            REASON_REFRESH_SAFETY_SOURCES,
             REASON_SYSTEM_ALLOW_LISTED,
             REASON_ALARM_MANAGER_ALARM_CLOCK,
             REASON_ALARM_MANAGER_WHILE_IDLE,
@@ -706,8 +705,8 @@
                 return "LOCALE_CHANGED";
             case REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
                 return "REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
-            case REASON_ACTION_REFRESH_SAFETY_SOURCES:
-                return "REASON_ACTION_REFRESH_SAFETY_SOURCES";
+            case REASON_REFRESH_SAFETY_SOURCES:
+                return "REASON_REFRESH_SAFETY_SOURCES";
             case REASON_SYSTEM_ALLOW_LISTED:
                 return "SYSTEM_ALLOW_LISTED";
             case REASON_ALARM_MANAGER_ALARM_CLOCK:
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index d93ad3c..b2ae8ee 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1209,18 +1209,16 @@
         synchronized (mLock) {
             mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
         }
-        // The user is starting but credential encrypted storage is still locked.
-        // Only direct-boot-aware jobs can safely run.
-        // Let's kick off any eligible jobs for this user.
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
     }
 
+    /** Start jobs after user is available, delayed by a few seconds since non-urgent. */
     @Override
-    public void onUserUnlocked(@NonNull TargetUser user) {
-        // The user is fully unlocked and credential encrypted storage is now decrypted.
-        // Direct-boot-UNaware jobs can now safely run.
-        // Let's kick off any outstanding jobs for this user.
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+    public void onUserCompletedEvent(@NonNull TargetUser user, UserCompletedEventType eventType) {
+        if (eventType.includesOnUserStarting() || eventType.includesOnUserUnlocked()) {
+            // onUserStarting: direct-boot-aware jobs can safely run
+            // onUserUnlocked: direct-boot-UNaware jobs can safely run.
+            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        }
     }
 
     @Override
diff --git a/api/Android.bp b/api/Android.bp
index d8727f9..3075d38 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -129,6 +129,7 @@
         "i18n.module.public.api",
     ],
     conditional_bootclasspath: [
+        "framework-auxiliary",
         "framework-supplementalapi",
     ],
     system_server_classpath: [
diff --git a/boot/Android.bp b/boot/Android.bp
index 3273f2c..8958d70 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -56,6 +56,10 @@
             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 e0b25c3..922ad16 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2108,6 +2108,8 @@
     field public static final int icon_frame = 16908350; // 0x102003e
     field public static final int input = 16908297; // 0x1020009
     field public static final int inputArea = 16908318; // 0x102001e
+    field public static final int inputExtractAccessories;
+    field public static final int inputExtractAction;
     field public static final int inputExtractEditText = 16908325; // 0x1020025
     field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
     field public static final int list = 16908298; // 0x102000a
@@ -4181,6 +4183,7 @@
     method public void openContextMenu(android.view.View);
     method public void openOptionsMenu();
     method public void overridePendingTransition(int, int);
+    method public void overridePendingTransition(int, int, int);
     method public void postponeEnterTransition();
     method public void recreate();
     method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
@@ -4484,6 +4487,7 @@
     method public static android.app.ActivityOptions makeBasic();
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+    method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int);
     method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
     method @java.lang.SafeVarargs public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View,java.lang.String>...);
@@ -11886,6 +11890,7 @@
     field public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
     field public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
     field public static final String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
+    field public static final String FEATURE_SENSOR_DYNAMIC_HEAD_TRACKER = "android.hardware.sensor.dynamic.head_tracker";
     field public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
     field public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
     field public static final String FEATURE_SENSOR_HEART_RATE_ECG = "android.hardware.sensor.heartrate.ecg";
@@ -18668,6 +18673,7 @@
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
     method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public void onPrepareStylusHandwriting();
     method public boolean onShowInputRequested(int, boolean);
     method public void onStartCandidatesView(android.view.inputmethod.EditorInfo, boolean);
     method public void onStartInput(android.view.inputmethod.EditorInfo, boolean);
@@ -20570,13 +20576,24 @@
   }
 
   public static final class EncoderProfiles.VideoProfile {
+    method public int getBitDepth();
     method public int getBitrate();
+    method public int getChromaSubsampling();
     method public int getCodec();
     method public int getFrameRate();
+    method public int getHdrFormat();
     method public int getHeight();
     method @NonNull public String getMediaType();
     method public int getProfile();
     method public int getWidth();
+    field public static final int HDR_DOLBY_VISION = 4; // 0x4
+    field public static final int HDR_HDR10 = 2; // 0x2
+    field public static final int HDR_HDR10PLUS = 3; // 0x3
+    field public static final int HDR_HLG = 1; // 0x1
+    field public static final int HDR_NONE = 0; // 0x0
+    field public static final int YUV_420 = 0; // 0x0
+    field public static final int YUV_422 = 1; // 0x1
+    field public static final int YUV_444 = 2; // 0x2
   }
 
   public class ExifInterface {
@@ -21359,11 +21376,14 @@
     field public static final int AVCProfileHigh422 = 32; // 0x20
     field public static final int AVCProfileHigh444 = 64; // 0x40
     field public static final int AVCProfileMain = 2; // 0x2
+    field public static final int DolbyVisionLevel8k30 = 1024; // 0x400
+    field public static final int DolbyVisionLevel8k60 = 2048; // 0x800
     field public static final int DolbyVisionLevelFhd24 = 4; // 0x4
     field public static final int DolbyVisionLevelFhd30 = 8; // 0x8
     field public static final int DolbyVisionLevelFhd60 = 16; // 0x10
     field public static final int DolbyVisionLevelHd24 = 1; // 0x1
     field public static final int DolbyVisionLevelHd30 = 2; // 0x2
+    field public static final int DolbyVisionLevelUhd120 = 512; // 0x200
     field public static final int DolbyVisionLevelUhd24 = 32; // 0x20
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
@@ -22682,12 +22702,15 @@
   }
 
   public final class MediaRecorder.VideoEncoder {
+    field public static final int AV1 = 8; // 0x8
     field public static final int DEFAULT = 0; // 0x0
+    field public static final int DOLBY_VISION = 7; // 0x7
     field public static final int H263 = 1; // 0x1
     field public static final int H264 = 2; // 0x2
     field public static final int HEVC = 5; // 0x5
     field public static final int MPEG_4_SP = 3; // 0x3
     field public static final int VP8 = 4; // 0x4
+    field public static final int VP9 = 6; // 0x6
   }
 
   public final class MediaRecorder.VideoSource {
@@ -26329,12 +26352,14 @@
 
   public static final class Ikev2VpnProfile.Builder {
     ctor public Ikev2VpnProfile.Builder(@NonNull String, @NonNull String);
+    ctor public Ikev2VpnProfile.Builder(@NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
     method @NonNull public android.net.Ikev2VpnProfile build();
     method @NonNull public android.net.Ikev2VpnProfile.Builder setAllowedAlgorithms(@NonNull java.util.List<java.lang.String>);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey, @Nullable java.security.cert.X509Certificate);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean);
+    method @NonNull public android.net.Ikev2VpnProfile.Builder setExcludeLocalRoutes(boolean);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
     method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
@@ -26480,6 +26505,7 @@
   }
 
   public abstract class PlatformVpnProfile {
+    method public final boolean getExcludeLocalRoutes();
     method public final int getType();
     method @NonNull public final String getTypeString();
     field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7
@@ -31438,7 +31464,7 @@
     method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
     method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
-    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<? super T>);
+    method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
@@ -31448,7 +31474,7 @@
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
     method @Deprecated @Nullable public java.io.Serializable readSerializable();
-    method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<? super T>);
+    method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
     method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -31657,6 +31683,7 @@
     method public boolean isDeviceLightIdleMode();
     method public boolean isIgnoringBatteryOptimizations(String);
     method public boolean isInteractive();
+    method public boolean isLowPowerStandbyEnabled();
     method public boolean isPowerSaveMode();
     method public boolean isRebootingUserspaceSupported();
     method @Deprecated public boolean isScreenOn();
@@ -31668,6 +31695,7 @@
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
+    field public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED = "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
     field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
     field @Deprecated public static final int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2; // 0x2
@@ -32392,6 +32420,7 @@
     method @Nullable public android.os.storage.StorageVolume getStorageVolume(@NonNull java.io.File);
     method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri);
     method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public java.util.List<android.os.storage.StorageVolume> getStorageVolumesIncludingSharedProfiles();
     method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException;
     method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
@@ -32425,6 +32454,7 @@
     method public String getDescription(android.content.Context);
     method @Nullable public java.io.File getDirectory();
     method @Nullable public String getMediaStoreVolumeName();
+    method @NonNull public android.os.UserHandle getOwner();
     method public String getState();
     method @Nullable public java.util.UUID getStorageUuid();
     method @Nullable public String getUuid();
@@ -37902,21 +37932,23 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
+    ctor @Deprecated public Dataset.Builder(@NonNull android.widget.RemoteViews);
+    ctor public Dataset.Builder(@NonNull android.service.autofill.Presentations);
     ctor public Dataset.Builder();
     method @NonNull public android.service.autofill.Dataset build();
     method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
+    method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field);
     method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
-    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
   }
 
   public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -37933,6 +37965,19 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.DateValueSanitizer> CREATOR;
   }
 
+  public final class Field {
+    method @Nullable public android.service.autofill.Presentations getPresentations();
+    method @Nullable public android.view.autofill.AutofillValue getValue();
+  }
+
+  public static final class Field.Builder {
+    ctor public Field.Builder();
+    method @NonNull public android.service.autofill.Field build();
+    method @NonNull public android.service.autofill.Field.Builder setFilter(@Nullable java.util.regex.Pattern);
+    method @NonNull public android.service.autofill.Field.Builder setPresentations(@NonNull android.service.autofill.Presentations);
+    method @NonNull public android.service.autofill.Field.Builder setValue(@NonNull android.view.autofill.AutofillValue);
+  }
+
   public final class FieldClassification {
     method @NonNull public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
   }
@@ -38015,11 +38060,14 @@
     method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
     method @NonNull public android.service.autofill.FillResponse build();
     method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
-    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
-    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
+    method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.service.autofill.Presentations);
     method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
+    method @NonNull public android.service.autofill.FillResponse.Builder setDialogHeader(@NonNull android.widget.RemoteViews);
     method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
+    method @NonNull public android.service.autofill.FillResponse.Builder setFillDialogTriggerIds(@NonNull android.view.autofill.AutofillId...);
     method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
     method @NonNull public android.service.autofill.FillResponse.Builder setFooter(@NonNull android.widget.RemoteViews);
     method @NonNull public android.service.autofill.FillResponse.Builder setHeader(@NonNull android.widget.RemoteViews);
@@ -38064,6 +38112,22 @@
   public interface OnClickAction {
   }
 
+  public final class Presentations {
+    method @Nullable public android.widget.RemoteViews getDialogPresentation();
+    method @Nullable public android.service.autofill.InlinePresentation getInlinePresentation();
+    method @Nullable public android.service.autofill.InlinePresentation getInlineTooltipPresentation();
+    method @Nullable public android.widget.RemoteViews getMenuPresentation();
+  }
+
+  public static final class Presentations.Builder {
+    ctor public Presentations.Builder();
+    method @NonNull public android.service.autofill.Presentations build();
+    method @NonNull public android.service.autofill.Presentations.Builder setDialogPresentation(@NonNull android.widget.RemoteViews);
+    method @NonNull public android.service.autofill.Presentations.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Presentations.Builder setInlineTooltipPresentation(@NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Presentations.Builder setMenuPresentation(@NonNull android.widget.RemoteViews);
+  }
+
   public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public RegexValidator(@NonNull android.view.autofill.AutofillId, @NonNull java.util.regex.Pattern);
     method public int describeContents();
@@ -49142,6 +49206,8 @@
   public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
     ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
     method public int describeContents();
+    method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration);
+    method public void notifyDetachedFromWindow();
     method public void release();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControlViewHost.SurfacePackage> CREATOR;
@@ -52768,6 +52834,9 @@
     method public default boolean setImeConsumesInput(boolean);
     method public boolean setSelection(int, int);
     method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
+    field public static final int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 8; // 0x8
+    field public static final int CURSOR_UPDATE_FILTER_EDITOR_BOUNDS = 4; // 0x4
+    field public static final int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 16; // 0x10
     field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
     field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
     field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
@@ -54273,7 +54342,6 @@
   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();
@@ -54318,7 +54386,8 @@
     method public abstract int getTextZoom();
     method public abstract boolean getUseWideViewPort();
     method public abstract String getUserAgentString();
-    method public void setAllowAlgorithmicDarkening(boolean);
+    method public boolean isAlgorithmicDarkeningAllowed();
+    method public void setAlgorithmicDarkeningAllowed(boolean);
     method public abstract void setAllowContentAccess(boolean);
     method public abstract void setAllowFileAccess(boolean);
     method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 1f40e59..8ea1abc 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -2,6 +2,7 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
     field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
   }
 
@@ -162,6 +163,8 @@
 
   public class LocationManager {
     method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull android.location.Location);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) public boolean isAutomotiveGnssSuspended();
+    method @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) public void setAutomotiveGnssSuspended(boolean);
   }
 
 }
@@ -538,10 +541,6 @@
     field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0
   }
 
-  public final class StorageVolume implements android.os.Parcelable {
-    method @NonNull public android.os.UserHandle getOwner();
-  }
-
 }
 
 package android.provider {
@@ -556,6 +555,26 @@
     field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
   }
 
+  public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+    field public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
+    field public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+    field public static final String BLE_SCAN_BALANCED_INTERVAL_MS = "ble_scan_balanced_interval_ms";
+    field public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
+    field public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS = "ble_scan_low_latency_interval_ms";
+    field public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS = "ble_scan_low_latency_window_ms";
+    field public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS = "ble_scan_low_power_interval_ms";
+    field public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
+    field public static final String BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
+    field public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
+    field public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
+  }
+
+  public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+    field public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
+    field public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+    field public static final String BLUETOOTH_NAME = "bluetooth_name";
+  }
+
 }
 
 package android.telephony {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 18a3f86..171ee9a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -28,6 +28,8 @@
     field public static final String ACCESS_ULTRASOUND = "android.permission.ACCESS_ULTRASOUND";
     field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
+    field public static final String ADD_ALWAYS_UNLOCKED_DISPLAY = "android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY";
+    field public static final String ADD_TRUSTED_DISPLAY = "android.permission.ADD_TRUSTED_DISPLAY";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
     field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
@@ -35,7 +37,6 @@
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
     field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
-    field public static final String AUTOMOTIVE_GNSS_CONTROLS = "android.permission.AUTOMOTIVE_GNSS_CONTROLS";
     field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BACKUP = "android.permission.BACKUP";
     field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
@@ -71,6 +72,7 @@
     field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
     field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
     field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE";
+    field public static final String BIND_TRACE_REPORT_SERVICE = "android.permission.BIND_TRACE_REPORT_SERVICE";
     field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
     field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
     field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
@@ -166,6 +168,7 @@
     field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
     field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
     field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
@@ -2110,6 +2113,7 @@
     method @Nullable public android.net.Uri getSliceUri();
     method @NonNull public String getSmartspaceTargetId();
     method @Nullable public String getSourceNotificationKey();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData getTemplateData();
     method @NonNull public android.os.UserHandle getUserHandle();
     method @Nullable public android.appwidget.AppWidgetProviderInfo getWidget();
     method public boolean isSensitive();
@@ -2140,6 +2144,14 @@
     field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
     field public static final int FEATURE_WEATHER = 1; // 0x1
     field public static final int FEATURE_WEATHER_ALERT = 10; // 0xa
+    field public static final int UI_TEMPLATE_CAROUSEL = 4; // 0x4
+    field public static final int UI_TEMPLATE_COMBINED_CARDS = 6; // 0x6
+    field public static final int UI_TEMPLATE_DEFAULT = 1; // 0x1
+    field public static final int UI_TEMPLATE_HEAD_TO_HEAD = 5; // 0x5
+    field public static final int UI_TEMPLATE_SUB_CARD = 7; // 0x7
+    field public static final int UI_TEMPLATE_SUB_IMAGE = 2; // 0x2
+    field public static final int UI_TEMPLATE_SUB_LIST = 3; // 0x3
+    field public static final int UI_TEMPLATE_UNDEFINED = 0; // 0x0
   }
 
   public static final class SmartspaceTarget.Builder {
@@ -2158,6 +2170,7 @@
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(boolean);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSliceUri(@NonNull android.net.Uri);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSourceNotificationKey(@NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setTemplateData(@Nullable android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData);
     method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setWidget(@NonNull android.appwidget.AppWidgetProviderInfo);
   }
 
@@ -2186,6 +2199,177 @@
 
 }
 
+package android.app.smartspace.uitemplatedata {
+
+  public final class SmartspaceCarouselUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getCarouselAction();
+    method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> getCarouselItems();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceCarouselUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceCarouselUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem>);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+  }
+
+  public static final class SmartspaceCarouselUiTemplateData.CarouselItem implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getImage();
+    method @Nullable public CharSequence getLowerText();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getTapAction();
+    method @Nullable public CharSequence getUpperText();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> CREATOR;
+  }
+
+  public static final class SmartspaceCarouselUiTemplateData.CarouselItem.Builder {
+    ctor public SmartspaceCarouselUiTemplateData.CarouselItem.Builder();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setImage(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setTapAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable CharSequence);
+  }
+
+  public final class SmartspaceCombinedCardsUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> getCombinedCardDataList();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCombinedCardsUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceCombinedCardsUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceCombinedCardsUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData>);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCombinedCardsUiTemplateData build();
+  }
+
+  public class SmartspaceDefaultUiTemplateData implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getPrimaryTapAction();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubTitleIcon();
+    method @Nullable public CharSequence getSubtitleText();
+    method @Nullable public CharSequence getSupplementalAlarmText();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSupplementalSubtitleIcon();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSupplementalSubtitleTapAction();
+    method @Nullable public CharSequence getSupplementalSubtitleText();
+    method public int getTemplateType();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getTitleIcon();
+    method @Nullable public CharSequence getTitleText();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> CREATOR;
+  }
+
+  public static class SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceDefaultUiTemplateData.Builder(int);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull CharSequence);
+  }
+
+  public final class SmartspaceHeadToHeadUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getHeadToHeadAction();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadFirstCompetitorIcon();
+    method @Nullable public CharSequence getHeadToHeadFirstCompetitorText();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadSecondCompetitorIcon();
+    method @Nullable public CharSequence getHeadToHeadSecondCompetitorText();
+    method @Nullable public CharSequence getHeadToHeadTitle();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceHeadToHeadUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceHeadToHeadUiTemplateData.Builder();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable CharSequence);
+  }
+
+  public final class SmartspaceIcon implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public CharSequence getContentDescription();
+    method @NonNull public android.graphics.drawable.Icon getIcon();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceIcon> CREATOR;
+  }
+
+  public static final class SmartspaceIcon.Builder {
+    ctor public SmartspaceIcon.Builder(@NonNull android.graphics.drawable.Icon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setContentDescription(@NonNull CharSequence);
+  }
+
+  public final class SmartspaceSubCardUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubCardAction();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubCardIcon();
+    method @Nullable public CharSequence getSubCardText();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceSubCardUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceSubCardUiTemplateData.Builder(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+  }
+
+  public final class SmartspaceSubImageUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubImageAction();
+    method @NonNull public java.util.List<java.lang.CharSequence> getSubImageTexts();
+    method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon> getSubImages();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceSubImageUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+  }
+
+  public final class SmartspaceSubListUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubListAction();
+    method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubListIcon();
+    method @NonNull public java.util.List<java.lang.CharSequence> getSubListTexts();
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData> CREATOR;
+  }
+
+  public static final class SmartspaceSubListUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
+    ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+  }
+
+  public final class SmartspaceTapAction implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.os.Bundle getExtras();
+    method @Nullable public CharSequence getId();
+    method @Nullable public android.content.Intent getIntent();
+    method @Nullable public android.app.PendingIntent getPendingIntent();
+    method @Nullable public android.os.UserHandle getUserHandle();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceTapAction> CREATOR;
+  }
+
+  public static final class SmartspaceTapAction.Builder {
+    ctor public SmartspaceTapAction.Builder(@NonNull CharSequence);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction build();
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setIntent(@NonNull android.content.Intent);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setPendingIntent(@NonNull android.app.PendingIntent);
+    method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setUserHandle(@Nullable android.os.UserHandle);
+  }
+
+}
+
 package android.app.time {
 
   public final class Capabilities {
@@ -2355,12 +2539,21 @@
 
   public class AppHibernationManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.List<java.lang.String> getHibernatingPackagesForUser();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.Map<java.lang.String,android.apphibernation.HibernationStats> getHibernationStatsForUser(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.Map<java.lang.String,android.apphibernation.HibernationStats> getHibernationStatsForUser();
     method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingForUser(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingGlobally(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingForUser(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingGlobally(@NonNull String, boolean);
   }
 
+  public final class HibernationStats implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getDiskBytesSaved();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.apphibernation.HibernationStats> CREATOR;
+  }
+
 }
 
 package android.companion {
@@ -2428,7 +2621,7 @@
   public static final class VirtualDeviceParams.Builder {
     ctor public VirtualDeviceParams.Builder();
     method @NonNull public android.companion.virtual.VirtualDeviceParams build();
-    method @NonNull @RequiresPermission(value="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY", conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
+    method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
   }
 
@@ -2575,7 +2768,6 @@
     field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
     field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
-    field public static final String ACTION_REFRESH_SAFETY_SOURCES = "android.intent.action.REFRESH_SAFETY_SOURCES";
     field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
     field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE";
@@ -2606,10 +2798,6 @@
     field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
     field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final String EXTRA_REASON = "android.intent.extra.REASON";
-    field public static final int EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA = 0; // 0x0
-    field public static final int EXTRA_REFRESH_REQUEST_TYPE_GET_DATA = 1; // 0x1
-    field public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE = "android.intent.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE";
-    field public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS = "android.intent.extra.REFRESH_SAFETY_SOURCE_IDS";
     field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
@@ -5366,7 +5554,6 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String, @NonNull android.location.LastLocationRequest);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
     method public boolean isAdasGnssLocationEnabled();
-    method @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS) public boolean isAutoGnssSuspended();
     method public boolean isExtraLocationControllerPackageEnabled();
     method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -5379,7 +5566,6 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAdasGnssLocationEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS) public void setAutoGnssSuspended(boolean);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
@@ -6290,6 +6476,7 @@
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @NonNull public java.util.List<java.lang.String> getAvailableExtensionInterfaceNames(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPid(@NonNull String);
     method public int getClientPriority(int, @Nullable String);
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
@@ -6462,7 +6649,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 @NonNull public java.util.List<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);
@@ -7707,7 +7894,7 @@
     method public boolean isLocked();
   }
 
-  public class FrontendStatusReadiness {
+  public final class FrontendStatusReadiness {
     method public int getStatusReadiness();
     method public int getStatusType();
     field public static final int FRONTEND_STATUS_READINESS_STABLE = 3; // 0x3
@@ -9113,6 +9300,7 @@
     field public static final int REASON_OTHER = 1; // 0x1
     field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
     field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+    field public static final int REASON_REFRESH_SAFETY_SOURCES = 208; // 0xd0
     field public static final int REASON_UNKNOWN = 0; // 0x0
     field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
     field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
@@ -9127,11 +9315,14 @@
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public boolean isLowPowerStandbySupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
     method @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_PREDICTION, android.Manifest.permission.DEVICE_POWER}) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setFullPowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyActiveDuringMaintenance(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
@@ -9324,6 +9515,8 @@
     method @NonNull public java.util.List<android.os.UserHandle> getAllProfiles();
     method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String, boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
@@ -10450,9 +10643,9 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+    ctor @Deprecated public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
     method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
-    method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+    method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
   }
 
   public abstract class InlineSuggestionRenderService extends android.app.Service {
@@ -11004,7 +11197,7 @@
     method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
     method public long getMaximumDataBlockSize();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
-    method @NonNull public String getPersistentDataPackageName();
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
     method public byte[] read();
     method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
     method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
@@ -11242,6 +11435,22 @@
 
 }
 
+package android.service.tracing {
+
+  public class TraceReportService extends android.app.Service {
+    ctor public TraceReportService();
+    method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public boolean onMessage(@NonNull android.os.Message);
+    method public void onReportTrace(@NonNull android.service.tracing.TraceReportService.TraceParams);
+  }
+
+  public static final class TraceReportService.TraceParams {
+    method @NonNull public android.os.ParcelFileDescriptor getFd();
+    method @NonNull public java.util.UUID getUuid();
+  }
+
+}
+
 package android.service.translation {
 
   public abstract class TranslationService extends android.app.Service {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2303ddb..39e12f4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -38,6 +38,7 @@
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
+    field public static final String SET_GAME_SERVICE = "android.permission.SET_GAME_SERVICE";
     field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
     field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -104,6 +105,7 @@
 
   @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     method public final boolean addDumpable(@NonNull android.util.Dumpable);
+    method public void dumpInternal(@NonNull String, @Nullable java.io.FileDescriptor, @NonNull java.io.PrintWriter, @Nullable String[]);
     method public void onMovedToDisplay(int, android.content.res.Configuration);
   }
 
@@ -151,7 +153,7 @@
 
   public class ActivityOptions {
     method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
-    method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+    method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
     method public static void setExitTransitionTimeout(long);
     method public void setLaunchActivityType(int);
@@ -275,6 +277,10 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
   }
 
+  public final class GameManager {
+    method public void setGameServiceProvider(@Nullable String);
+  }
+
   public abstract class HomeVisibilityListener {
     ctor public HomeVisibilityListener();
     method public abstract void onHomeVisibilityChanged(boolean);
@@ -1713,7 +1719,9 @@
   }
 
   public final class PowerManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
     field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
+    field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
   }
 
   public class Process {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 897bab4..8234f03 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -29,10 +29,20 @@
 }
 
 filegroup {
+    name: "IBinaryTransparencyService.aidl",
+    srcs: ["com/android/internal/os/IBinaryTransparencyService.aidl"],
+}
+
+filegroup {
     name: "ITracingServiceProxy.aidl",
     srcs: ["android/tracing/ITracingServiceProxy.aidl"],
 }
 
+filegroup {
+    name: "TraceReportParams.aidl",
+    srcs: ["android/tracing/TraceReportParams.aidl"],
+}
+
 // These are subset of framework-core-sources that are needed by the
 // android.test.mock library. The implementation of android.test.mock references
 // private members of various components to allow mocking of classes that cannot
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 38138d8..530666b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -33,12 +33,16 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.StyleRes;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UiContext;
 import android.app.VoiceInteractor.Request;
 import android.app.admin.DevicePolicyManager;
 import android.app.assist.AssistContent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -788,6 +792,16 @@
     private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
     private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
 
+    /**
+     * After {@link Build.VERSION_CODES#TIRAMISU},
+     * {@link #dump(String, FileDescriptor, PrintWriter, String[])} is not called if
+     * {@code dumpsys activity} is called with some special arguments.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    @VisibleForTesting
+    private static final long DUMP_IGNORES_SPECIAL_ARGS = 149254050L;
+
     private static class ManagedDialog {
         Dialog mDialog;
         Bundle mArgs;
@@ -6131,8 +6145,31 @@
      * the outgoing activity.  Use 0 for no animation.
      */
     public void overridePendingTransition(int enterAnim, int exitAnim) {
-        ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(),
-                enterAnim, exitAnim);
+        overridePendingTransition(enterAnim, exitAnim, 0);
+    }
+
+    /**
+     * Call immediately after one of the flavors of {@link #startActivity(Intent)}
+     * or {@link #finish} to specify an explicit transition animation to
+     * perform next.
+     *
+     * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative
+     * to using this with starting activities is to supply the desired animation
+     * information through a {@link ActivityOptions} bundle to
+     * {@link #startActivity(Intent, Bundle)} or a related function.  This allows
+     * you to specify a custom animation even when starting an activity from
+     * outside the context of the current top activity.
+     *
+     * @param enterAnim A resource ID of the animation resource to use for
+     * the incoming activity.  Use 0 for no animation.
+     * @param exitAnim A resource ID of the animation resource to use for
+     * the outgoing activity.  Use 0 for no animation.
+     * @param backgroundColor The background color to use for the background during the animation if
+     * the animation requires a background. Set to 0 to not override the default color.
+     */
+    public void overridePendingTransition(int enterAnim, int exitAnim, int backgroundColor) {
+        ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), enterAnim,
+                exitAnim, backgroundColor);
     }
 
     /**
@@ -7079,7 +7116,18 @@
 
     /**
      * Print the Activity's state into the given stream.  This gets invoked if
-     * you run "adb shell dumpsys activity &lt;activity_component_name&gt;".
+     * you run <code>adb shell dumpsys activity &lt;activity_component_name&gt;</code>.
+     *
+     * <p>This method won't be called if the app targets
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU} or later if the dump request starts with one
+     * of the following arguments:
+     * <ul>
+     *   <li>--autofill
+     *   <li>--contentcapture
+     *   <li>--translation
+     *   <li>--list-dumpables
+     *   <li>--dump-dumpable
+     * </ul>
      *
      * @param prefix Desired prefix to prepend at each line of output.
      * @param fd The raw file descriptor that the dump is being sent to.
@@ -7106,11 +7154,20 @@
         return mDumpableContainer.addDumpable(dumpable);
     }
 
-    void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
+    /**
+     * This is the real method called by {@code ActivityThread}, but it's also exposed so
+     * CTS can test for the special args cases.
+     *
+     * @hide
+     */
+    @TestApi
+    @VisibleForTesting
+    @SuppressLint("OnNameExpected")
+    public void dumpInternal(@NonNull String prefix,
+            @SuppressLint("UseParcelFileDescriptor") @Nullable FileDescriptor fd,
             @NonNull PrintWriter writer, @Nullable String[] args) {
-        String innerPrefix = prefix + "  ";
-
-        if (args != null && args.length > 0) {
+        if (args != null && args.length > 0
+                && CompatChanges.isChangeEnabled(DUMP_IGNORES_SPECIAL_ARGS)) {
             // Handle special cases
             switch (args[0]) {
                 case "--autofill":
@@ -7145,6 +7202,12 @@
                     return;
             }
         }
+        dump(prefix, fd, writer, args);
+    }
+
+    void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
+            @NonNull PrintWriter writer, @Nullable String[] args) {
+        String innerPrefix = prefix + "  ";
 
         writer.print(prefix); writer.print("Local Activity ");
                 writer.print(Integer.toHexString(System.identityHashCode(this)));
@@ -7161,11 +7224,6 @@
                 writer.println(mChangingConfigurations);
         writer.print(innerPrefix); writer.print("mCurrentConfig=");
                 writer.println(mCurrentConfig);
-        if (getResources().hasOverrideDisplayAdjustments()) {
-            writer.print(innerPrefix);
-            writer.print("FixedRotationAdjustments=");
-            writer.println(getResources().getDisplayAdjustments().getFixedRotationAdjustments());
-        }
 
         mFragments.dumpLoaders(innerPrefix, fd, writer, args);
         mFragments.getFragmentManager().dump(innerPrefix, fd, writer, args);
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index eb4a355..605a1fa 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -428,11 +428,11 @@
         }
     }
 
-    void overridePendingTransition(IBinder token, String packageName,
-            int enterAnim, int exitAnim) {
+    void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim,
+            int backgroundColor) {
         try {
             getActivityClientController().overridePendingTransition(token, packageName,
-                    enterAnim, exitAnim);
+                    enterAnim, exitAnim, backgroundColor);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 5e5649f..e405b60 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -126,6 +126,12 @@
     public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
 
     /**
+     * Custom background color for animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_BACKGROUND_COLOR = "android:activity.backgroundColor";
+
+    /**
      * Bitmap for thumbnail animation.
      * @hide
      */
@@ -389,6 +395,7 @@
     private int mCustomEnterResId;
     private int mCustomExitResId;
     private int mCustomInPlaceResId;
+    private int mCustomBackgroundColor;
     private Bitmap mThumbnail;
     private int mStartX;
     private int mStartY;
@@ -453,7 +460,27 @@
      */
     public static ActivityOptions makeCustomAnimation(Context context,
             int enterResId, int exitResId) {
-        return makeCustomAnimation(context, enterResId, exitResId, null, null, null);
+        return makeCustomAnimation(context, enterResId, exitResId, 0, null, null);
+    }
+
+    /**
+     * Create an ActivityOptions specifying a custom animation to run when
+     * the activity is displayed.
+     *
+     * @param context Who is defining this.  This is the application that the
+     * animation resources will be loaded from.
+     * @param enterResId A resource ID of the animation resource to use for
+     * the incoming activity.  Use 0 for no animation.
+     * @param exitResId A resource ID of the animation resource to use for
+     * the outgoing activity.  Use 0 for no animation.
+     * @param backgroundColor The background color to use for the background during the animation if
+     * the animation requires a background. Set to 0 to not override the default color.
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     */
+    public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
+            int enterResId, int exitResId, int backgroundColor) {
+        return makeCustomAnimation(context, enterResId, exitResId, backgroundColor, null, null);
     }
 
     /**
@@ -477,12 +504,14 @@
      */
     @UnsupportedAppUsage
     public static ActivityOptions makeCustomAnimation(Context context,
-            int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) {
+            int enterResId, int exitResId, int backgroundColor, Handler handler,
+            OnAnimationStartedListener listener) {
         ActivityOptions opts = new ActivityOptions();
         opts.mPackageName = context.getPackageName();
         opts.mAnimationType = ANIM_CUSTOM;
         opts.mCustomEnterResId = enterResId;
         opts.mCustomExitResId = exitResId;
+        opts.mCustomBackgroundColor = backgroundColor;
         opts.setOnAnimationStartedListener(handler, listener);
         return opts;
     }
@@ -510,11 +539,11 @@
      */
     @TestApi
     public static @NonNull ActivityOptions makeCustomAnimation(@NonNull Context context,
-            int enterResId, int exitResId, @Nullable Handler handler,
+            int enterResId, int exitResId, int backgroundColor, @Nullable Handler handler,
             @Nullable OnAnimationStartedListener startedListener,
             @Nullable OnAnimationFinishedListener finishedListener) {
-        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
-                startedListener);
+        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, backgroundColor,
+                handler, startedListener);
         opts.setOnAnimationFinishedListener(handler, finishedListener);
         return opts;
     }
@@ -547,8 +576,8 @@
             int enterResId, int exitResId, @Nullable Handler handler,
             @Nullable OnAnimationStartedListener startedListener,
             @Nullable OnAnimationFinishedListener finishedListener) {
-        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
-                startedListener, finishedListener);
+        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, 0,
+                handler, startedListener, finishedListener);
         opts.mOverrideTaskTransition = true;
         return opts;
     }
@@ -1243,6 +1272,11 @@
         return mCustomInPlaceResId;
     }
 
+    /** @hide */
+    public int getCustomBackgroundColor() {
+        return mCustomBackgroundColor;
+    }
+
     /**
      * The thumbnail is copied into a hardware bitmap when it is bundled and sent to the system, so
      * it should always be backed by a HardwareBuffer on the other end.
@@ -1775,6 +1809,7 @@
             case ANIM_CUSTOM:
                 mCustomEnterResId = otherOptions.mCustomEnterResId;
                 mCustomExitResId = otherOptions.mCustomExitResId;
+                mCustomBackgroundColor = otherOptions.mCustomBackgroundColor;
                 mThumbnail = null;
                 if (mAnimationStartedListener != null) {
                     try {
@@ -1862,6 +1897,7 @@
             case ANIM_CUSTOM:
                 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
                 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+                b.putInt(KEY_ANIM_BACKGROUND_COLOR, mCustomBackgroundColor);
                 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea62714..3b2176e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -175,7 +175,6 @@
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayAdjustments;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.View;
@@ -598,12 +597,6 @@
         /** The options for scene transition. */
         ActivityOptions mActivityOptions;
 
-        /**
-         * If non-null, the activity is launching with a specified rotation, the adjustments should
-         * be consumed before activity creation.
-         */
-        FixedRotationAdjustments mPendingFixedRotationAdjustments;
-
         /** Whether this activiy was launched from a bubble. */
         boolean mLaunchedFromBubble;
 
@@ -625,8 +618,7 @@
                 PersistableBundle persistentState, List<ResultInfo> pendingResults,
                 List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
                 boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
-                IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
-                IBinder shareableActivityToken, boolean launchedFromBubble) {
+                IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble) {
             this.token = token;
             this.assistToken = assistToken;
             this.shareableActivityToken = shareableActivityToken;
@@ -646,7 +638,6 @@
             this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
                     compatInfo);
             mActivityOptions = activityOptions;
-            mPendingFixedRotationAdjustments = fixedRotationAdjustments;
             mLaunchedFromBubble = launchedFromBubble;
             init();
         }
@@ -3512,21 +3503,6 @@
         mH.sendMessage(msg);
     }
 
-    private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
-        if (DEBUG_MESSAGES) Slog.v(
-                TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
-                        "seq= " + seq);
-        Message msg = Message.obtain();
-        msg.what = what;
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = obj;
-        args.argi1 = arg1;
-        args.argi2 = arg2;
-        args.argi3 = seq;
-        msg.obj = args;
-        mH.sendMessage(msg);
-    }
-
     final void scheduleContextCleanup(ContextImpl context, String who,
             String what) {
         ContextCleanupInfo cci = new ContextCleanupInfo();
@@ -3536,64 +3512,6 @@
         sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    /**
-     * Applies the rotation adjustments to override display information in resources belong to the
-     * provided token. If the token is activity token, the adjustments also apply to application
-     * because the appearance of activity is usually more sensitive to the application resources.
-     *
-     * @param token The token to apply the adjustments.
-     * @param fixedRotationAdjustments The information to override the display adjustments of
-     *                                 corresponding resources. If it is null, the exiting override
-     *                                 will be cleared.
-     */
-    @Override
-    public void handleFixedRotationAdjustments(@NonNull IBinder token,
-            @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
-        final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
-                ? displayAdjustments -> displayAdjustments
-                        .setFixedRotationAdjustments(fixedRotationAdjustments)
-                : null;
-        if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
-            // No resources are associated with the token.
-            return;
-        }
-        if (mActivities.get(token) == null) {
-            // Nothing to do for application if it is not an activity token.
-            return;
-        }
-
-        overrideApplicationDisplayAdjustments(token, override);
-    }
-
-    /**
-     * Applies the last override to application resources for compatibility. Because the Resources
-     * of Display can be from application, e.g.
-     *   applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
-     * and the deprecated usage:
-     *   applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
-     *
-     * @param token The owner and target of the override.
-     * @param override The display adjustments override for application resources. If it is null,
-     *                 the override of the token will be removed and pop the last one to use.
-     */
-    private void overrideApplicationDisplayAdjustments(@NonNull IBinder token,
-            @Nullable Consumer<DisplayAdjustments> override) {
-        final Consumer<DisplayAdjustments> appOverride;
-        if (mActiveRotationAdjustments == null) {
-            mActiveRotationAdjustments = new ArrayList<>(2);
-        }
-        if (override != null) {
-            mActiveRotationAdjustments.add(Pair.create(token, override));
-            appOverride = override;
-        } else {
-            mActiveRotationAdjustments.removeIf(adjustmentsPair -> adjustmentsPair.first == token);
-            appOverride = mActiveRotationAdjustments.isEmpty()
-                    ? null
-                    : mActiveRotationAdjustments.get(mActiveRotationAdjustments.size() - 1).second;
-        }
-        mInitialApplication.getResources().overrideDisplayAdjustments(appOverride);
-    }
-
     /**  Core implementation of activity launch. */
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         ActivityInfo aInfo = r.activityInfo;
@@ -3811,19 +3729,6 @@
         ContextImpl appContext = ContextImpl.createActivityContext(
                 this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
 
-        // The rotation adjustments must be applied before creating the activity, so the activity
-        // can get the adjusted display info during creation.
-        if (r.mPendingFixedRotationAdjustments != null) {
-            // The adjustments should have been set by handleLaunchActivity, so the last one is the
-            // override for activity resources.
-            if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
-                mResourcesManager.overrideTokenDisplayAdjustments(r.token,
-                        mActiveRotationAdjustments.get(
-                                mActiveRotationAdjustments.size() - 1).second);
-            }
-            r.mPendingFixedRotationAdjustments = null;
-        }
-
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
         // the "debug.use-second-display" system property as a substring, then show
@@ -3859,13 +3764,6 @@
             mProfiler.startProfiling();
         }
 
-        if (r.mPendingFixedRotationAdjustments != null) {
-            // The rotation adjustments must be applied before handling configuration, so process
-            // level display metrics can be adjusted.
-            overrideApplicationDisplayAdjustments(r.token, adjustments ->
-                    adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));
-        }
-
         // Make sure we are running with the most recent config.
         mConfigurationController.handleConfigurationChanged(null, null);
 
@@ -4693,7 +4591,7 @@
             if (r != null && r.activity != null) {
                 PrintWriter pw = new FastPrintWriter(new FileOutputStream(
                         info.fd.getFileDescriptor()));
-                r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
+                r.activity.dumpInternal(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
                 pw.flush();
             }
         } finally {
@@ -5974,13 +5872,6 @@
         final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                 amOverrideConfig, contextThemeWrapperOverrideConfig);
         mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
-        final Resources res = activity.getResources();
-        if (res.hasOverrideDisplayAdjustments()) {
-            // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
-            // configuration of activity may be sent later than the adjustments. In this case, the
-            // adjustments need to be updated for the consistency of display info.
-            res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
-        }
 
         activity.mConfigChangeFlags = 0;
         activity.mCurrentConfig = new Configuration(newConfig);
@@ -7643,8 +7534,7 @@
                 // We need to apply this change to the resources immediately, because upon returning
                 // the view hierarchy will be informed about it.
                 if (mResourcesManager.applyConfigurationToResources(globalConfig,
-                        null /* compat */,
-                        mInitialApplication.getResources().getDisplayAdjustments())) {
+                        null /* compat */)) {
                     mConfigurationController.updateLocaleListFromAppContext(
                             mInitialApplication.getApplicationContext());
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0081234..0d1bc05 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1327,9 +1327,17 @@
      */
     public static final int OP_ESTABLISH_VPN_MANAGER = AppProtoEnums.APP_OP_ESTABLISH_VPN_MANAGER;
 
+    /**
+     * Access restricted settings.
+     *
+     * @hide
+     */
+    public static final int OP_ACCESS_RESTRICTED_SETTINGS =
+            AppProtoEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 119;
+    public static final int _NUM_OP = 120;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1784,6 +1792,14 @@
     @SystemApi
     public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
 
+    /**
+     * Limit user accessing restricted settings.
+     *
+     * @hide
+     */
+    public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
+            "android:access_restricted_settings";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2004,6 +2020,7 @@
             OP_NEARBY_WIFI_DEVICES,             // OP_NEARBY_WIFI_DEVICES
             OP_ESTABLISH_VPN_SERVICE,           // OP_ESTABLISH_VPN_SERVICE
             OP_ESTABLISH_VPN_MANAGER,           // OP_ESTABLISH_VPN_MANAGER
+            OP_ACCESS_RESTRICTED_SETTINGS,      // OP_ACCESS_RESTRICTED_SETTINGS
     };
 
     /**
@@ -2129,6 +2146,7 @@
             OPSTR_NEARBY_WIFI_DEVICES,
             OPSTR_ESTABLISH_VPN_SERVICE,
             OPSTR_ESTABLISH_VPN_MANAGER,
+            OPSTR_ACCESS_RESTRICTED_SETTINGS,
     };
 
     /**
@@ -2255,6 +2273,7 @@
             "NEARBY_WIFI_DEVICES",
             "ESTABLISH_VPN_SERVICE",
             "ESTABLISH_VPN_MANAGER",
+            "ACCESS_RESTRICTED_SETTINGS",
     };
 
     /**
@@ -2382,6 +2401,7 @@
             Manifest.permission.NEARBY_WIFI_DEVICES,
             null, // no permission for OP_ESTABLISH_VPN_SERVICE
             null, // no permission for OP_ESTABLISH_VPN_MANAGER
+            null, // no permission for OP_ACCESS_RESTRICTED_SETTINGS,
     };
 
     /**
@@ -2509,6 +2529,7 @@
             null, // NEARBY_WIFI_DEVICES
             null, // ESTABLISH_VPN_SERVICE
             null, // ESTABLISH_VPN_MANAGER
+            null, // ACCESS_RESTRICTED_SETTINGS,
     };
 
     /**
@@ -2635,6 +2656,7 @@
             null, // NEARBY_WIFI_DEVICES
             null, // ESTABLISH_VPN_SERVICE
             null, // ESTABLISH_VPN_MANAGER
+            null, // ACCESS_RESTRICTED_SETTINGS,
     };
 
     /**
@@ -2760,6 +2782,7 @@
             AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES
             AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_SERVICE
             AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_MANAGER
+            AppOpsManager.MODE_ALLOWED, // ACCESS_RESTRICTED_SETTINGS,
     };
 
     /**
@@ -2889,6 +2912,7 @@
             false, // NEARBY_WIFI_DEVICES
             false, // OP_ESTABLISH_VPN_SERVICE
             false, // OP_ESTABLISH_VPN_MANAGER
+            true, // ACCESS_RESTRICTED_SETTINGS
     };
 
     /**
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index d365269..65e6ab7 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -27,7 +27,6 @@
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.util.MergedConfiguration;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 import android.view.SurfaceControl;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
 
@@ -187,10 +186,6 @@
     /** Deliver app configuration change notification. */
     public abstract void handleConfigurationChanged(Configuration config);
 
-    /** Apply addition adjustments to override display information. */
-    public abstract void handleFixedRotationAdjustments(IBinder token,
-            FixedRotationAdjustments fixedRotationAdjustments);
-
     /**
      * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
      * provided token.
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 58f60a6..1a77b65 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -185,14 +185,7 @@
 
             final Application app = mActivityThread.getApplication();
             final Resources appResources = app.getResources();
-            if (appResources.hasOverrideDisplayAdjustments()) {
-                // The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
-                // but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
-                // configuration also needs to set to the adjustments for consistency.
-                appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
-            }
-            mResourcesManager.applyConfigurationToResources(config, compat,
-                    appResources.getDisplayAdjustments());
+            mResourcesManager.applyConfigurationToResources(config, compat);
             updateLocaleListFromAppContext(app.getApplicationContext());
 
             if (mConfiguration == null) {
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 76471d3..289b348 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UserHandleAware;
 import android.content.Context;
 import android.os.Handler;
@@ -204,4 +205,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Sets the game service provider to the given package name for test only.
+     *
+     * <p>Passing in {@code null} will clear a previously set value.
+     * @hide
+     */
+    @TestApi
+    public void setGameServiceProvider(@Nullable String packageName) {
+        try {
+            mService.setGameServiceProvider(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 83c57c5..396e552 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -112,7 +112,7 @@
      * calls, so this method should be the same as them to keep the invocation order.
      */
     void overridePendingTransition(in IBinder token, in String packageName,
-            int enterAnim, int exitAnim);
+            int enterAnim, int exitAnim, int backgroundColor);
     int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
 
     /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 57de8c7..3ea07676 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -29,4 +29,5 @@
     boolean getAngleEnabled(String packageName, int userId);
     void setGameState(String packageName, in GameState gameState, int userId);
     GameModeInfo getGameModeInfo(String packageName, int userId);
+    void setGameServiceProvider(String packageName);
 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index f360bbed..2a1883d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -65,7 +65,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.WeakHashMap;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 /** @hide */
@@ -343,6 +342,25 @@
         return dm;
     }
 
+    /**
+     * Like getDisplayMetrics, but will adjust the result based on the display information in
+     * config. This is used to make sure that the global configuration matches the activity's
+     * apparent display.
+     */
+    private DisplayMetrics getDisplayMetrics(Configuration config) {
+        final DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
+        final DisplayMetrics dm = new DisplayMetrics();
+        final DisplayInfo displayInfo = displayManagerGlobal != null
+                ? displayManagerGlobal.getDisplayInfo(mResDisplayId) : null;
+        if (displayInfo != null) {
+            displayInfo.getAppMetrics(dm,
+                    DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS.getCompatibilityInfo(), config);
+        } else {
+            dm.setToDefaults();
+        }
+        return dm;
+    }
+
     private static void applyDisplayMetricsToConfiguration(@NonNull DisplayMetrics dm,
             @NonNull Configuration config) {
         config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
@@ -1311,12 +1329,6 @@
 
     public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat) {
-        return applyConfigurationToResources(config, compat, null /* adjustments */);
-    }
-
-    /** Applies the global configuration to the managed resources. */
-    public final boolean applyConfigurationToResources(@NonNull Configuration config,
-            @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
         synchronized (mLock) {
             try {
                 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
@@ -1346,12 +1358,7 @@
                     applyAllPendingAppInfoUpdates();
                 }
 
-                DisplayMetrics displayMetrics = getDisplayMetrics();
-                if (adjustments != null) {
-                    // Currently the only case where the adjustment takes effect is to simulate
-                    // placing an app in a rotated display.
-                    adjustments.adjustGlobalAppMetrics(displayMetrics);
-                }
+                final DisplayMetrics displayMetrics = getDisplayMetrics(config);
                 Resources.updateSystemConfiguration(config, displayMetrics, compat);
 
                 ApplicationPackageManager.configurationChanged();
@@ -1591,41 +1598,6 @@
         }
     }
 
-    /**
-     * Overrides the display adjustments of all resources which are associated with the given token.
-     *
-     * @param token The token that owns the resources.
-     * @param override The operation to override the existing display adjustments. If it is null,
-     *                 the override adjustments will be cleared.
-     * @return {@code true} if the override takes effect.
-     */
-    public boolean overrideTokenDisplayAdjustments(IBinder token,
-            @Nullable Consumer<DisplayAdjustments> override) {
-        boolean handled = false;
-        synchronized (mLock) {
-            final ActivityResources tokenResources = mActivityResourceReferences.get(token);
-            if (tokenResources == null) {
-                return false;
-            }
-            final ArrayList<ActivityResource> resourcesRefs = tokenResources.activityResources;
-            for (int i = resourcesRefs.size() - 1; i >= 0; i--) {
-                final ActivityResource activityResource = resourcesRefs.get(i);
-                if (activityResource.overrideDisplayId != null) {
-                    // This resource overrides the display of the token so we should not be
-                    // modifying its display adjustments here.
-                    continue;
-                }
-
-                final Resources res = activityResource.resources.get();
-                if (res != null) {
-                    res.overrideDisplayAdjustments(override);
-                    handled = true;
-                }
-            }
-        }
-        return handled;
-    }
-
     private class UpdateHandler implements Resources.UpdateCallbacks {
 
         /**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c17d1a3..bbdd705 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -215,6 +215,7 @@
 import android.telephony.MmsManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyRegistryManager;
+import android.transparency.BinaryTransparencyManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -245,6 +246,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.graphics.fonts.IFontManager;
 import com.android.internal.net.INetworkWatchlistManager;
+import com.android.internal.os.IBinaryTransparencyService;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.policy.PhoneLayoutInflater;
 import com.android.internal.util.Preconditions;
@@ -496,6 +498,17 @@
                 return new DropBoxManager(ctx, service);
             }});
 
+        registerService(Context.BINARY_TRANSPARENCY_SERVICE, BinaryTransparencyManager.class,
+                new CachedServiceFetcher<BinaryTransparencyManager>() {
+            @Override
+            public BinaryTransparencyManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(
+                        Context.BINARY_TRANSPARENCY_SERVICE);
+                IBinaryTransparencyService service = IBinaryTransparencyService.Stub.asInterface(b);
+                return new BinaryTransparencyManager(ctx, service);
+            }});
+
         registerService(Context.INPUT_SERVICE, InputManager.class,
                 new StaticServiceFetcher<InputManager>() {
             @Override
@@ -1021,15 +1034,14 @@
             }});
 
         registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
-                new CachedServiceFetcher<PersistentDataBlockManager>() {
+                new StaticServiceFetcher<PersistentDataBlockManager>() {
             @Override
-            public PersistentDataBlockManager createService(ContextImpl ctx)
-                    throws ServiceNotFoundException {
+            public PersistentDataBlockManager createService() throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                 IPersistentDataBlockService persistentDataBlockService =
                         IPersistentDataBlockService.Stub.asInterface(b);
                 if (persistentDataBlockService != null) {
-                    return new PersistentDataBlockManager(ctx, persistentDataBlockService);
+                    return new PersistentDataBlockManager(persistentDataBlockService);
                 } else {
                     // not supported
                     return null;
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4ff7924..b791f05 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -37,6 +37,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.WireTypeMismatchException;
 import android.view.DisplayInfo;
+import android.view.Surface;
 import android.view.WindowManager;
 
 import java.io.IOException;
@@ -74,6 +75,13 @@
     private final Rect mMaxBounds = new Rect();
 
     /**
+     * The rotation of this window's apparent display. This can differ from mRotation in some
+     * situations (like letterbox).
+     */
+    @Surface.Rotation
+    private int mDisplayRotation = ROTATION_UNDEFINED;
+
+    /**
      * The current rotation of this window container relative to the default
      * orientation of the display it is on (regardless of how deep in the hierarchy
      * it is). It is used by the configuration hierarchy to apply rotation-dependent
@@ -196,6 +204,9 @@
     /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
      * @hide */
     public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 7;
+    /** Bit that indicates that the apparent-display changed.
+     * @hide */
+    public static final int WINDOW_CONFIG_DISPLAY_ROTATION = 1 << 8;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
@@ -207,6 +218,7 @@
             WINDOW_CONFIG_ALWAYS_ON_TOP,
             WINDOW_CONFIG_ROTATION,
             WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
+            WINDOW_CONFIG_DISPLAY_ROTATION,
     })
     public @interface WindowConfig {}
 
@@ -237,6 +249,7 @@
         dest.writeInt(mAlwaysOnTop);
         dest.writeInt(mRotation);
         dest.writeInt(mDisplayWindowingMode);
+        dest.writeInt(mDisplayRotation);
     }
 
     /** @hide */
@@ -249,6 +262,7 @@
         mAlwaysOnTop = source.readInt();
         mRotation = source.readInt();
         mDisplayWindowingMode = source.readInt();
+        mDisplayRotation = source.readInt();
     }
 
     @Override
@@ -318,6 +332,14 @@
     }
 
     /**
+     * Sets the apparent display cutout.
+     * @hide
+     */
+    public void setDisplayRotation(@Surface.Rotation int rotation) {
+        mDisplayRotation = rotation;
+    }
+
+    /**
      * Sets whether this window should be always on top.
      * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
      * @hide
@@ -359,6 +381,14 @@
         return mMaxBounds;
     }
 
+    /**
+     * @see #setDisplayRotation
+     * @hide
+     */
+    public @Surface.Rotation int getDisplayRotation() {
+        return mDisplayRotation;
+    }
+
     public int getRotation() {
         return mRotation;
     }
@@ -413,6 +443,7 @@
         setBounds(other.mBounds);
         setAppBounds(other.mAppBounds);
         setMaxBounds(other.mMaxBounds);
+        setDisplayRotation(other.mDisplayRotation);
         setWindowingMode(other.mWindowingMode);
         setActivityType(other.mActivityType);
         setAlwaysOnTop(other.mAlwaysOnTop);
@@ -431,6 +462,7 @@
         setAppBounds(null);
         setBounds(null);
         setMaxBounds(null);
+        setDisplayRotation(ROTATION_UNDEFINED);
         setWindowingMode(WINDOWING_MODE_UNDEFINED);
         setActivityType(ACTIVITY_TYPE_UNDEFINED);
         setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
@@ -485,6 +517,11 @@
             changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
             setDisplayWindowingMode(delta.mDisplayWindowingMode);
         }
+        if (delta.mDisplayRotation != ROTATION_UNDEFINED
+                && delta.mDisplayRotation != mDisplayRotation) {
+            changed |= WINDOW_CONFIG_DISPLAY_ROTATION;
+            setDisplayRotation(delta.mDisplayRotation);
+        }
         return changed;
     }
 
@@ -517,6 +554,9 @@
         if ((mask & WINDOW_CONFIG_DISPLAY_WINDOWING_MODE) != 0) {
             setDisplayWindowingMode(delta.mDisplayWindowingMode);
         }
+        if ((mask & WINDOW_CONFIG_DISPLAY_ROTATION) != 0) {
+            setDisplayRotation(delta.mDisplayRotation);
+        }
     }
 
     /**
@@ -573,6 +613,11 @@
             changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
         }
 
+        if ((compareUndefined || other.mDisplayRotation != ROTATION_UNDEFINED)
+                && mDisplayRotation != other.mDisplayRotation) {
+            changes |= WINDOW_CONFIG_DISPLAY_ROTATION;
+        }
+
         return changes;
     }
 
@@ -620,8 +665,11 @@
         if (n != 0) return n;
         n = mRotation - that.mRotation;
         if (n != 0) return n;
+
         n = mDisplayWindowingMode - that.mDisplayWindowingMode;
         if (n != 0) return n;
+        n = mDisplayRotation - that.mDisplayRotation;
+        if (n != 0) return n;
 
         // if (n != 0) return n;
         return n;
@@ -650,6 +698,7 @@
         result = 31 * result + mAlwaysOnTop;
         result = 31 * result + mRotation;
         result = 31 * result + mDisplayWindowingMode;
+        result = 31 * result + mDisplayRotation;
         return result;
     }
 
@@ -659,6 +708,8 @@
         return "{ mBounds=" + mBounds
                 + " mAppBounds=" + mAppBounds
                 + " mMaxBounds=" + mMaxBounds
+                + " mDisplayRotation=" + (mRotation == ROTATION_UNDEFINED
+                        ? "undefined" : rotationToString(mDisplayRotation))
                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
                 + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
                 + " mActivityType=" + activityTypeToString(mActivityType)
diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
deleted file mode 100644
index 4c06333..0000000
--- a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import android.annotation.Nullable;
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import java.util.Objects;
-
-/**
- * The request to update display adjustments for a rotated activity or window token.
- * @hide
- */
-public class FixedRotationAdjustmentsItem extends ClientTransactionItem {
-
-    /** The token who may have {@link android.content.res.Resources}. */
-    private IBinder mToken;
-
-    /**
-     * The adjustments for the display adjustments of resources. If it is null, the existing
-     * rotation adjustments will be dropped to restore natural state.
-     */
-    private FixedRotationAdjustments mFixedRotationAdjustments;
-
-    private FixedRotationAdjustmentsItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    public static FixedRotationAdjustmentsItem obtain(IBinder token,
-            FixedRotationAdjustments fixedRotationAdjustments) {
-        FixedRotationAdjustmentsItem instance =
-                ObjectPool.obtain(FixedRotationAdjustmentsItem.class);
-        if (instance == null) {
-            instance = new FixedRotationAdjustmentsItem();
-        }
-        instance.mToken = token;
-        instance.mFixedRotationAdjustments = fixedRotationAdjustments;
-
-        return instance;
-    }
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token,
-            PendingTransactionActions pendingActions) {
-        client.handleFixedRotationAdjustments(mToken, mFixedRotationAdjustments);
-    }
-
-    @Override
-    public void recycle() {
-        mToken = null;
-        mFixedRotationAdjustments = null;
-        ObjectPool.recycle(this);
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mToken);
-        dest.writeTypedObject(mFixedRotationAdjustments, flags);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        final FixedRotationAdjustmentsItem other = (FixedRotationAdjustmentsItem) o;
-        return Objects.equals(mToken, other.mToken)
-                && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + Objects.hashCode(mToken);
-        result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
-        return result;
-    }
-
-    private FixedRotationAdjustmentsItem(Parcel in) {
-        mToken = in.readStrongBinder();
-        mFixedRotationAdjustments = in.readTypedObject(FixedRotationAdjustments.CREATOR);
-    }
-
-    public static final Creator<FixedRotationAdjustmentsItem> CREATOR =
-            new Creator<FixedRotationAdjustmentsItem>() {
-        public FixedRotationAdjustmentsItem createFromParcel(Parcel in) {
-            return new FixedRotationAdjustmentsItem(in);
-        }
-
-        public FixedRotationAdjustmentsItem[] newArray(int size) {
-            return new FixedRotationAdjustmentsItem[size];
-        }
-    };
-}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 0a2503c..abf1058 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -38,7 +38,6 @@
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.Trace;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
@@ -78,7 +77,6 @@
      * optimization for quick look up of the interface so the field is ignored for comparison.
      */
     private IActivityClientController mActivityClientController;
-    private FixedRotationAdjustments mFixedRotationAdjustments;
 
     @Override
     public void preExecute(ClientTransactionHandler client, IBinder token) {
@@ -97,8 +95,7 @@
         ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                 mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                 mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
-                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
-                mLaunchedFromBubble);
+                client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble);
         client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -121,8 +118,7 @@
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
             List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
             boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
-            IActivityClientController activityClientController,
-            FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
+            IActivityClientController activityClientController, IBinder shareableActivityToken,
             boolean launchedFromBubble) {
         LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
         if (instance == null) {
@@ -131,7 +127,7 @@
         setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
                 voiceInteractor, procState, state, persistentState, pendingResults,
                 pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
-                activityClientController, fixedRotationAdjustments, shareableActivityToken,
+                activityClientController, shareableActivityToken,
                 launchedFromBubble);
 
         return instance;
@@ -140,7 +136,7 @@
     @Override
     public void recycle() {
         setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
-                null, false, null, null, null, null, null, false);
+                null, false, null, null, null, null, false);
         ObjectPool.recycle(this);
     }
 
@@ -168,7 +164,6 @@
         dest.writeTypedObject(mProfilerInfo, flags);
         dest.writeStrongBinder(mAssistToken);
         dest.writeStrongInterface(mActivityClientController);
-        dest.writeTypedObject(mFixedRotationAdjustments, flags);
         dest.writeStrongBinder(mShareableActivityToken);
         dest.writeBoolean(mLaunchedFromBubble);
     }
@@ -188,7 +183,7 @@
                 in.readTypedObject(ProfilerInfo.CREATOR),
                 in.readStrongBinder(),
                 IActivityClientController.Stub.asInterface(in.readStrongBinder()),
-                in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
+                in.readStrongBinder(),
                 in.readBoolean());
     }
 
@@ -232,7 +227,6 @@
                 && mIsForward == other.mIsForward
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo)
                 && Objects.equals(mAssistToken, other.mAssistToken)
-                && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
                 && Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
     }
 
@@ -254,7 +248,6 @@
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + Objects.hashCode(mProfilerInfo);
         result = 31 * result + Objects.hashCode(mAssistToken);
-        result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
         result = 31 * result + Objects.hashCode(mShareableActivityToken);
         return result;
     }
@@ -292,7 +285,6 @@
                 + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
                 + ",pendingNewIntents=" + mPendingNewIntents + ",options=" + mActivityOptions
                 + ",profilerInfo=" + mProfilerInfo + ",assistToken=" + mAssistToken
-                + ",rotationAdj=" + mFixedRotationAdjustments
                 + ",shareableActivityToken=" + mShareableActivityToken + "}";
     }
 
@@ -304,8 +296,7 @@
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
             ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
             IBinder assistToken, IActivityClientController activityClientController,
-            FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
-            boolean launchedFromBubble) {
+            IBinder shareableActivityToken, boolean launchedFromBubble) {
         instance.mIntent = intent;
         instance.mIdent = ident;
         instance.mInfo = info;
@@ -324,7 +315,6 @@
         instance.mProfilerInfo = profilerInfo;
         instance.mAssistToken = assistToken;
         instance.mActivityClientController = activityClientController;
-        instance.mFixedRotationAdjustments = fixedRotationAdjustments;
         instance.mShareableActivityToken = shareableActivityToken;
         instance.mLaunchedFromBubble = launchedFromBubble;
     }
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
index 8e98535..78f51be 100644
--- a/core/java/android/app/smartspace/SmartspaceTarget.java
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.net.Uri;
@@ -131,6 +132,9 @@
     @Nullable
     private final AppWidgetProviderInfo mWidget;
 
+    @Nullable
+    private final SmartspaceDefaultUiTemplateData mTemplateData;
+
     public static final int FEATURE_UNDEFINED = 0;
     public static final int FEATURE_WEATHER = 1;
     public static final int FEATURE_CALENDAR = 2;
@@ -189,6 +193,32 @@
     public @interface FeatureType {
     }
 
+    public static final int UI_TEMPLATE_UNDEFINED = 0;
+    public static final int UI_TEMPLATE_DEFAULT = 1;
+    public static final int UI_TEMPLATE_SUB_IMAGE = 2;
+    public static final int UI_TEMPLATE_SUB_LIST = 3;
+    public static final int UI_TEMPLATE_CAROUSEL = 4;
+    public static final int UI_TEMPLATE_HEAD_TO_HEAD = 5;
+    public static final int UI_TEMPLATE_COMBINED_CARDS = 6;
+    public static final int UI_TEMPLATE_SUB_CARD = 7;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"UI_TEMPLATE_"}, value = {
+            UI_TEMPLATE_UNDEFINED,
+            UI_TEMPLATE_DEFAULT,
+            UI_TEMPLATE_SUB_IMAGE,
+            UI_TEMPLATE_SUB_LIST,
+            UI_TEMPLATE_CAROUSEL,
+            UI_TEMPLATE_HEAD_TO_HEAD,
+            UI_TEMPLATE_COMBINED_CARDS,
+            UI_TEMPLATE_SUB_CARD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiTemplateType {
+    }
+
     private SmartspaceTarget(Parcel in) {
         this.mSmartspaceTargetId = in.readString();
         this.mHeaderAction = in.readTypedObject(SmartspaceAction.CREATOR);
@@ -207,6 +237,7 @@
         this.mAssociatedSmartspaceTargetId = in.readString();
         this.mSliceUri = in.readTypedObject(Uri.CREATOR);
         this.mWidget = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
+        this.mTemplateData = in.readTypedObject(SmartspaceDefaultUiTemplateData.CREATOR);
     }
 
     private SmartspaceTarget(String smartspaceTargetId,
@@ -217,7 +248,7 @@
             boolean shouldShowExpanded, String sourceNotificationKey,
             ComponentName componentName, UserHandle userHandle,
             String associatedSmartspaceTargetId, Uri sliceUri,
-            AppWidgetProviderInfo widget) {
+            AppWidgetProviderInfo widget, SmartspaceDefaultUiTemplateData templateData) {
         mSmartspaceTargetId = smartspaceTargetId;
         mHeaderAction = headerAction;
         mBaseAction = baseAction;
@@ -235,6 +266,7 @@
         mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
         mSliceUri = sliceUri;
         mWidget = widget;
+        mTemplateData = templateData;
     }
 
     /**
@@ -371,6 +403,14 @@
     }
 
     /**
+     * Returns the UI template data.
+     */
+    @Nullable
+    public SmartspaceDefaultUiTemplateData getTemplateData() {
+        return mTemplateData;
+    }
+
+    /**
      * @see Parcelable.Creator
      */
     @NonNull
@@ -405,6 +445,7 @@
         dest.writeString(this.mAssociatedSmartspaceTargetId);
         dest.writeTypedObject(this.mSliceUri, flags);
         dest.writeTypedObject(this.mWidget, flags);
+        dest.writeTypedObject(this.mTemplateData, flags);
     }
 
     @Override
@@ -432,6 +473,7 @@
                 + ", mAssociatedSmartspaceTargetId='" + mAssociatedSmartspaceTargetId + '\''
                 + ", mSliceUri=" + mSliceUri
                 + ", mWidget=" + mWidget
+                + ", mTemplateData=" + mTemplateData
                 + '}';
     }
 
@@ -457,7 +499,8 @@
                 && Objects.equals(mAssociatedSmartspaceTargetId,
                 that.mAssociatedSmartspaceTargetId)
                 && Objects.equals(mSliceUri, that.mSliceUri)
-                && Objects.equals(mWidget, that.mWidget);
+                && Objects.equals(mWidget, that.mWidget)
+                && Objects.equals(mTemplateData, that.mTemplateData);
     }
 
     @Override
@@ -465,7 +508,7 @@
         return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
                 mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
                 mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
-                mAssociatedSmartspaceTargetId, mSliceUri, mWidget);
+                mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
     }
 
     /**
@@ -476,6 +519,9 @@
     @SystemApi
     public static final class Builder {
         private final String mSmartspaceTargetId;
+        private final ComponentName mComponentName;
+        private final UserHandle mUserHandle;
+
         private SmartspaceAction mHeaderAction;
         private SmartspaceAction mBaseAction;
         private long mCreationTimeMillis;
@@ -487,11 +533,10 @@
         private boolean mSensitive;
         private boolean mShouldShowExpanded;
         private String mSourceNotificationKey;
-        private final ComponentName mComponentName;
-        private final UserHandle mUserHandle;
         private String mAssociatedSmartspaceTargetId;
         private Uri mSliceUri;
         private AppWidgetProviderInfo mWidget;
+        private SmartspaceDefaultUiTemplateData mTemplateData;
 
         /**
          * A builder for {@link SmartspaceTarget}.
@@ -640,6 +685,16 @@
         }
 
         /**
+         * Sets the UI template data.
+         */
+        @NonNull
+        public Builder setTemplateData(
+                @Nullable SmartspaceDefaultUiTemplateData templateData) {
+            mTemplateData = templateData;
+            return this;
+        }
+
+        /**
          * Builds a new {@link SmartspaceTarget}.
          *
          * @throws IllegalStateException when non null fields are set as null.
@@ -655,7 +710,7 @@
                     mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
                     mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
                     mSourceNotificationKey, mComponentName, mUserHandle,
-                    mAssociatedSmartspaceTargetId, mSliceUri, mWidget);
+                    mAssociatedSmartspaceTargetId, mSliceUri, mWidget, mTemplateData);
         }
     }
 }
diff --git a/core/java/android/app/smartspace/SmartspaceUtils.java b/core/java/android/app/smartspace/SmartspaceUtils.java
new file mode 100644
index 0000000..f058ffa
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceUtils.java
@@ -0,0 +1,37 @@
+/*
+ * 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.smartspace;
+
+import android.annotation.Nullable;
+
+/**
+ * Utilities for Smartspace data.
+ *
+ * @hide
+ */
+public final class SmartspaceUtils {
+
+    private SmartspaceUtils() {
+    }
+
+    /** Returns true if the passed-in {@link CharSequence}s are equal. */
+    public static boolean isEqual(@Nullable CharSequence cs1, @Nullable CharSequence cs2) {
+        if ((cs1 == null && cs2 != null) || (cs1 != null && cs2 == null)) return false;
+        if (cs1 == null && cs2 == null) return true;
+        return cs1.toString().contentEquals(cs2);
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
new file mode 100644
index 0000000..c4c4fde
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
@@ -0,0 +1,360 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the carousel Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    /** Lists of {@link CarouselItem}. */
+    @NonNull
+    private final List<CarouselItem> mCarouselItems;
+
+    /** Tap action for the entire carousel secondary card, including the blank space */
+    @Nullable
+    private final SmartspaceTapAction mCarouselAction;
+
+    SmartspaceCarouselUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mCarouselItems = in.createTypedArrayList(CarouselItem.CREATOR);
+        mCarouselAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+    }
+
+    private SmartspaceCarouselUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @NonNull List<CarouselItem> carouselItems,
+            @Nullable SmartspaceTapAction carouselAction) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mCarouselItems = carouselItems;
+        mCarouselAction = carouselAction;
+    }
+
+    @NonNull
+    public List<CarouselItem> getCarouselItems() {
+        return mCarouselItems;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getCarouselAction() {
+        return mCarouselAction;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceCarouselUiTemplateData> CREATOR =
+            new Creator<SmartspaceCarouselUiTemplateData>() {
+                @Override
+                public SmartspaceCarouselUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceCarouselUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceCarouselUiTemplateData[] newArray(int size) {
+                    return new SmartspaceCarouselUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeTypedList(mCarouselItems);
+        out.writeTypedObject(mCarouselAction, flags);
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceCarouselUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceCarouselUiTemplateData that = (SmartspaceCarouselUiTemplateData) o;
+        return mCarouselItems.equals(that.mCarouselItems) && Objects.equals(mCarouselAction,
+                that.mCarouselAction);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCarouselItems, mCarouselAction);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceCarouselUiTemplateData{"
+                + "mCarouselItems=" + mCarouselItems
+                + ", mCarouselActions=" + mCarouselAction
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceCarouselUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private final List<CarouselItem> mCarouselItems;
+        private SmartspaceTapAction mCarouselAction;
+
+        /**
+         * A builder for {@link SmartspaceCarouselUiTemplateData}.
+         */
+        public Builder(@NonNull List<CarouselItem> carouselItems) {
+            super(SmartspaceTarget.UI_TEMPLATE_CAROUSEL);
+            mCarouselItems = Objects.requireNonNull(carouselItems);
+        }
+
+        /**
+         * Sets the card tap action.
+         */
+        @NonNull
+        public Builder setCarouselAction(@NonNull SmartspaceTapAction carouselAction) {
+            mCarouselAction = carouselAction;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceCarouselUiTemplateData instance.
+         *
+         * @throws IllegalStateException if the carousel data is invalid.
+         */
+        @NonNull
+        public SmartspaceCarouselUiTemplateData build() {
+            if (mCarouselItems.isEmpty()) {
+                throw new IllegalStateException("Carousel data is empty");
+            }
+            return new SmartspaceCarouselUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    mCarouselItems,
+                    mCarouselAction);
+        }
+    }
+
+    /** Holds all the relevant data needed to render a carousel item. */
+    public static final class CarouselItem implements Parcelable {
+
+        /** Text which is above the image item. */
+        @Nullable
+        private final CharSequence mUpperText;
+
+        /** Image item. Can be empty. */
+        @Nullable
+        private final SmartspaceIcon mImage;
+
+        /** Text which is under the image item. */
+        @Nullable
+        private final CharSequence mLowerText;
+
+        /**
+         * Tap action for this {@link CarouselItem} instance. {@code mCarouselAction} is used if not
+         * being set.
+         */
+        @Nullable
+        private final SmartspaceTapAction mTapAction;
+
+        CarouselItem(@NonNull Parcel in) {
+            mUpperText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            mImage = in.readTypedObject(SmartspaceIcon.CREATOR);
+            mLowerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            mTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+        }
+
+        private CarouselItem(@Nullable CharSequence upperText, @Nullable SmartspaceIcon image,
+                @Nullable CharSequence lowerText, @Nullable SmartspaceTapAction tapAction) {
+            mUpperText = upperText;
+            mImage = image;
+            mLowerText = lowerText;
+            mTapAction = tapAction;
+        }
+
+        @Nullable
+        public CharSequence getUpperText() {
+            return mUpperText;
+        }
+
+        @Nullable
+        public SmartspaceIcon getImage() {
+            return mImage;
+        }
+
+        @Nullable
+        public CharSequence getLowerText() {
+            return mLowerText;
+        }
+
+        @Nullable
+        public SmartspaceTapAction getTapAction() {
+            return mTapAction;
+        }
+
+        /**
+         * @see Parcelable.Creator
+         */
+        @NonNull
+        public static final Creator<CarouselItem> CREATOR =
+                new Creator<CarouselItem>() {
+                    @Override
+                    public CarouselItem createFromParcel(Parcel in) {
+                        return new CarouselItem(in);
+                    }
+
+                    @Override
+                    public CarouselItem[] newArray(int size) {
+                        return new CarouselItem[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            TextUtils.writeToParcel(mUpperText, out, flags);
+            out.writeTypedObject(mImage, flags);
+            TextUtils.writeToParcel(mLowerText, out, flags);
+            out.writeTypedObject(mTapAction, flags);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CarouselItem)) return false;
+            CarouselItem that = (CarouselItem) o;
+            return SmartspaceUtils.isEqual(mUpperText, that.mUpperText) && Objects.equals(
+                    mImage,
+                    that.mImage) && SmartspaceUtils.isEqual(mLowerText, that.mLowerText)
+                    && Objects.equals(mTapAction, that.mTapAction);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mUpperText, mImage, mLowerText, mTapAction);
+        }
+
+        @Override
+        public String toString() {
+            return "CarouselItem{"
+                    + "mUpperText=" + mUpperText
+                    + ", mImage=" + mImage
+                    + ", mLowerText=" + mLowerText
+                    + ", mTapAction=" + mTapAction
+                    + '}';
+        }
+
+        /**
+         * A builder for {@link CarouselItem} object.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final class Builder {
+
+            private CharSequence mUpperText;
+            private SmartspaceIcon mImage;
+            private CharSequence mLowerText;
+            private SmartspaceTapAction mTapAction;
+
+            /**
+             * Sets the upper text.
+             */
+            @NonNull
+            public Builder setUpperText(@Nullable CharSequence upperText) {
+                mUpperText = upperText;
+                return this;
+            }
+
+            /**
+             * Sets the image.
+             */
+            @NonNull
+            public Builder setImage(@Nullable SmartspaceIcon image) {
+                mImage = image;
+                return this;
+            }
+
+
+            /**
+             * Sets the lower text.
+             */
+            @NonNull
+            public Builder setLowerText(@Nullable CharSequence lowerText) {
+                mLowerText = lowerText;
+                return this;
+            }
+
+            /**
+             * Sets the tap action.
+             */
+            @NonNull
+            public Builder setTapAction(@Nullable SmartspaceTapAction tapAction) {
+                mTapAction = tapAction;
+                return this;
+            }
+
+            /**
+             * Builds a new CarouselItem instance.
+             *
+             * @throws IllegalStateException if all the rendering data is empty.
+             */
+            @NonNull
+            public CarouselItem build() {
+                if (TextUtils.isEmpty(mUpperText) && mImage == null && TextUtils.isEmpty(
+                        mLowerText)) {
+                    throw new IllegalStateException("Carousel data is empty");
+                }
+                return new CarouselItem(mUpperText, mImage, mLowerText, mTapAction);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
new file mode 100644
index 0000000..7e2f74e
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
@@ -0,0 +1,155 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the combined-card Ui
+ * Template.
+ *
+ * We only support 1 sub-list card combined with 1 carousel card. And we may expand our supported
+ * combinations in the future.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceCombinedCardsUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    /** A list of secondary cards. */
+    @NonNull
+    private final List<SmartspaceDefaultUiTemplateData> mCombinedCardDataList;
+
+    SmartspaceCombinedCardsUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mCombinedCardDataList = in.createTypedArrayList(SmartspaceDefaultUiTemplateData.CREATOR);
+    }
+
+    private SmartspaceCombinedCardsUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mCombinedCardDataList = combinedCardDataList;
+    }
+
+    @NonNull
+    public List<SmartspaceDefaultUiTemplateData> getCombinedCardDataList() {
+        return mCombinedCardDataList;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceCombinedCardsUiTemplateData> CREATOR =
+            new Creator<SmartspaceCombinedCardsUiTemplateData>() {
+                @Override
+                public SmartspaceCombinedCardsUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceCombinedCardsUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceCombinedCardsUiTemplateData[] newArray(int size) {
+                    return new SmartspaceCombinedCardsUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeTypedList(mCombinedCardDataList);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceCombinedCardsUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceCombinedCardsUiTemplateData that = (SmartspaceCombinedCardsUiTemplateData) o;
+        return mCombinedCardDataList.equals(that.mCombinedCardDataList);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCombinedCardDataList);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceCombinedCardsUiTemplateData{"
+                + "mCombinedCardDataList=" + mCombinedCardDataList
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceCombinedCardsUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private final List<SmartspaceDefaultUiTemplateData> mCombinedCardDataList;
+
+        /**
+         * A builder for {@link SmartspaceCombinedCardsUiTemplateData}.
+         */
+        public Builder(@NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) {
+            super(SmartspaceTarget.UI_TEMPLATE_COMBINED_CARDS);
+            mCombinedCardDataList = Objects.requireNonNull(combinedCardDataList);
+        }
+
+        /**
+         * Builds a new SmartspaceCombinedCardsUiTemplateData instance.
+         *
+         * @throws IllegalStateException if any required non-null field is null
+         */
+        @NonNull
+        public SmartspaceCombinedCardsUiTemplateData build() {
+            if (mCombinedCardDataList == null) {
+                throw new IllegalStateException("Please assign a value to all @NonNull args.");
+            }
+            return new SmartspaceCombinedCardsUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    mCombinedCardDataList);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
new file mode 100644
index 0000000..742d5c9
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget.UiTemplateType;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the default Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressLint("ParcelNotFinal")
+public class SmartspaceDefaultUiTemplateData implements Parcelable {
+
+    /**
+     * {@link UiTemplateType} indicating the template type of this template data.
+     *
+     * @see UiTemplateType
+     */
+    @UiTemplateType
+    private final int mTemplateType;
+
+    /**
+     * Title text and title icon are shown at the first row. When both are absent, the date view
+     * will be used, which has its own tap action applied to the title area.
+     */
+    @Nullable
+    private final CharSequence mTitleText;
+
+    @Nullable
+    private final SmartspaceIcon mTitleIcon;
+
+    /** Subtitle text and icon are shown at the second row. */
+    @Nullable
+    private final CharSequence mSubtitleText;
+
+    @Nullable
+    private final SmartspaceIcon mSubTitleIcon;
+
+    /**
+     * Primary tap action for the entire card, including the blank spaces, except: 1. When title is
+     * absent, the date view's default tap action is used; 2. Supplemental subtitle uses its own tap
+     * action if being set; 3. Secondary card uses its own tap action if being set.
+     */
+    @Nullable
+    private final SmartspaceTapAction mPrimaryTapAction;
+
+    /**
+     * Supplemental subtitle text and icon are shown at the second row following the subtitle text.
+     * Mainly used for weather info on non-weather card.
+     */
+    @Nullable
+    private final CharSequence mSupplementalSubtitleText;
+
+    @Nullable
+    private final SmartspaceIcon mSupplementalSubtitleIcon;
+
+    /**
+     * Tap action for the supplemental subtitle's text and icon. Will use the primary tap action if
+     * not being set.
+     */
+    @Nullable
+    private final SmartspaceTapAction mSupplementalSubtitleTapAction;
+
+    /**
+     * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next
+     * alarm".
+     */
+    @Nullable
+    private final CharSequence mSupplementalAlarmText;
+
+    SmartspaceDefaultUiTemplateData(@NonNull Parcel in) {
+        mTemplateType = in.readInt();
+        mTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSubTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mPrimaryTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+        mSupplementalSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSupplementalSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mSupplementalSubtitleTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+        mSupplementalAlarmText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+    }
+
+    /**
+     * Should ONLY used by subclasses. For the general instance creation, please use
+     * SmartspaceDefaultUiTemplateData.Builder.
+     */
+    SmartspaceDefaultUiTemplateData(@UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText) {
+        mTemplateType = templateType;
+        mTitleText = titleText;
+        mTitleIcon = titleIcon;
+        mSubtitleText = subtitleText;
+        mSubTitleIcon = subTitleIcon;
+        mPrimaryTapAction = primaryTapAction;
+        mSupplementalSubtitleText = supplementalSubtitleText;
+        mSupplementalSubtitleIcon = supplementalSubtitleIcon;
+        mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+        mSupplementalAlarmText = supplementalAlarmText;
+    }
+
+    @UiTemplateType
+    public int getTemplateType() {
+        return mTemplateType;
+    }
+
+    @Nullable
+    public CharSequence getTitleText() {
+        return mTitleText;
+    }
+
+    @Nullable
+    public SmartspaceIcon getTitleIcon() {
+        return mTitleIcon;
+    }
+
+    @Nullable
+    public CharSequence getSubtitleText() {
+        return mSubtitleText;
+    }
+
+    @Nullable
+    public SmartspaceIcon getSubTitleIcon() {
+        return mSubTitleIcon;
+    }
+
+    @Nullable
+    public CharSequence getSupplementalSubtitleText() {
+        return mSupplementalSubtitleText;
+    }
+
+    @Nullable
+    public SmartspaceIcon getSupplementalSubtitleIcon() {
+        return mSupplementalSubtitleIcon;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getPrimaryTapAction() {
+        return mPrimaryTapAction;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getSupplementalSubtitleTapAction() {
+        return mSupplementalSubtitleTapAction;
+    }
+
+    @Nullable
+    public CharSequence getSupplementalAlarmText() {
+        return mSupplementalAlarmText;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceDefaultUiTemplateData> CREATOR =
+            new Creator<SmartspaceDefaultUiTemplateData>() {
+                @Override
+                public SmartspaceDefaultUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceDefaultUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceDefaultUiTemplateData[] newArray(int size) {
+                    return new SmartspaceDefaultUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mTemplateType);
+        TextUtils.writeToParcel(mTitleText, out, flags);
+        out.writeTypedObject(mTitleIcon, flags);
+        TextUtils.writeToParcel(mSubtitleText, out, flags);
+        out.writeTypedObject(mSubTitleIcon, flags);
+        out.writeTypedObject(mPrimaryTapAction, flags);
+        TextUtils.writeToParcel(mSupplementalSubtitleText, out, flags);
+        out.writeTypedObject(mSupplementalSubtitleIcon, flags);
+        out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
+        TextUtils.writeToParcel(mSupplementalAlarmText, out, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceDefaultUiTemplateData)) return false;
+        SmartspaceDefaultUiTemplateData that = (SmartspaceDefaultUiTemplateData) o;
+        return mTemplateType == that.mTemplateType && SmartspaceUtils.isEqual(mTitleText,
+                that.mTitleText)
+                && Objects.equals(mTitleIcon, that.mTitleIcon)
+                && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
+                && Objects.equals(mSubTitleIcon, that.mSubTitleIcon)
+                && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
+                && SmartspaceUtils.isEqual(mSupplementalSubtitleText,
+                that.mSupplementalSubtitleText)
+                && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon)
+                && Objects.equals(mSupplementalSubtitleTapAction,
+                that.mSupplementalSubtitleTapAction)
+                && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubTitleIcon,
+                mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon,
+                mSupplementalSubtitleTapAction, mSupplementalAlarmText);
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceDefaultUiTemplateData{"
+                + "mTemplateType=" + mTemplateType
+                + ", mTitleText=" + mTitleText
+                + ", mTitleIcon=" + mTitleIcon
+                + ", mSubtitleText=" + mSubtitleText
+                + ", mSubTitleIcon=" + mSubTitleIcon
+                + ", mPrimaryTapAction=" + mPrimaryTapAction
+                + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
+                + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
+                + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction
+                + ", mSupplementalAlarmText=" + mSupplementalAlarmText
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceDefaultUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("StaticFinalBuilder")
+    public static class Builder {
+        @UiTemplateType
+        private final int mTemplateType;
+        private CharSequence mTitleText;
+        private SmartspaceIcon mTitleIcon;
+        private CharSequence mSubtitleText;
+        private SmartspaceIcon mSubTitleIcon;
+        private SmartspaceTapAction mPrimaryTapAction;
+        private CharSequence mSupplementalSubtitleText;
+        private SmartspaceIcon mSupplementalSubtitleIcon;
+        private SmartspaceTapAction mSupplementalSubtitleTapAction;
+        private CharSequence mSupplementalAlarmText;
+
+        /**
+         * A builder for {@link SmartspaceDefaultUiTemplateData}.
+         *
+         * @param templateType the {@link UiTemplateType} of this template data.
+         */
+        public Builder(@UiTemplateType int templateType) {
+            mTemplateType = templateType;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @UiTemplateType
+        @SuppressLint("GetterOnBuilder")
+        int getTemplateType() {
+            return mTemplateType;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        CharSequence getTitleText() {
+            return mTitleText;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SmartspaceIcon getTitleIcon() {
+            return mTitleIcon;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        CharSequence getSubtitleText() {
+            return mSubtitleText;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SmartspaceIcon getSubTitleIcon() {
+            return mSubTitleIcon;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SmartspaceTapAction getPrimaryTapAction() {
+            return mPrimaryTapAction;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        CharSequence getSupplementalSubtitleText() {
+            return mSupplementalSubtitleText;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SmartspaceIcon getSupplementalSubtitleIcon() {
+            return mSupplementalSubtitleIcon;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SmartspaceTapAction getSupplementalSubtitleTapAction() {
+            return mSupplementalSubtitleTapAction;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        CharSequence getSupplementalAlarmText() {
+            return mSupplementalAlarmText;
+        }
+
+        /**
+         * Sets the card title.
+         */
+        @NonNull
+        public Builder setTitleText(@NonNull CharSequence titleText) {
+            mTitleText = titleText;
+            return this;
+        }
+
+        /**
+         * Sets the card title icon.
+         */
+        @NonNull
+        public Builder setTitleIcon(@NonNull SmartspaceIcon titleIcon) {
+            mTitleIcon = titleIcon;
+            return this;
+        }
+
+        /**
+         * Sets the card subtitle.
+         */
+        @NonNull
+        public Builder setSubtitleText(@NonNull CharSequence subtitleText) {
+            mSubtitleText = subtitleText;
+            return this;
+        }
+
+        /**
+         * Sets the card subtitle icon.
+         */
+        @NonNull
+        public Builder setSubTitleIcon(@NonNull SmartspaceIcon subTitleIcon) {
+            mSubTitleIcon = subTitleIcon;
+            return this;
+        }
+
+        /**
+         * Sets the card primary tap action.
+         */
+        @NonNull
+        public Builder setPrimaryTapAction(@NonNull SmartspaceTapAction primaryTapAction) {
+            mPrimaryTapAction = primaryTapAction;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental subtitle text.
+         */
+        @NonNull
+        public Builder setSupplementalSubtitleText(@NonNull CharSequence supplementalSubtitleText) {
+            mSupplementalSubtitleText = supplementalSubtitleText;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental subtitle icon.
+         */
+        @NonNull
+        public Builder setSupplementalSubtitleIcon(
+                @NonNull SmartspaceIcon supplementalSubtitleIcon) {
+            mSupplementalSubtitleIcon = supplementalSubtitleIcon;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not
+         * being
+         * set.
+         */
+        @NonNull
+        public Builder setSupplementalSubtitleTapAction(
+                @NonNull SmartspaceTapAction supplementalSubtitleTapAction) {
+            mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental alarm text.
+         */
+        @NonNull
+        public Builder setSupplementalAlarmText(@NonNull CharSequence supplementalAlarmText) {
+            mSupplementalAlarmText = supplementalAlarmText;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceDefaultUiTemplateData instance.
+         */
+        @NonNull
+        public SmartspaceDefaultUiTemplateData build() {
+            return new SmartspaceDefaultUiTemplateData(mTemplateType, mTitleText, mTitleIcon,
+                    mSubtitleText, mSubTitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
+                    mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
+                    mSupplementalAlarmText);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
new file mode 100644
index 0000000..c76af27
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
@@ -0,0 +1,286 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the head-to-head Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    @Nullable
+    private final CharSequence mHeadToHeadTitle;
+    @Nullable
+    private final SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
+    @Nullable
+    private final SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
+    @Nullable
+    private final CharSequence mHeadToHeadFirstCompetitorText;
+    @Nullable
+    private final CharSequence mHeadToHeadSecondCompetitorText;
+
+    /** Tap action for the head-to-head secondary card. */
+    @Nullable
+    private final SmartspaceTapAction mHeadToHeadAction;
+
+    SmartspaceHeadToHeadUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mHeadToHeadTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mHeadToHeadFirstCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mHeadToHeadSecondCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mHeadToHeadFirstCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mHeadToHeadSecondCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mHeadToHeadAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+    }
+
+    private SmartspaceHeadToHeadUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @Nullable CharSequence headToHeadTitle,
+            @Nullable SmartspaceIcon headToHeadFirstCompetitorIcon,
+            @Nullable SmartspaceIcon headToHeadSecondCompetitorIcon,
+            @Nullable CharSequence headToHeadFirstCompetitorText,
+            @Nullable CharSequence headToHeadSecondCompetitorText,
+            @Nullable SmartspaceTapAction headToHeadAction) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mHeadToHeadTitle = headToHeadTitle;
+        mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
+        mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
+        mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText;
+        mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText;
+        mHeadToHeadAction = headToHeadAction;
+    }
+
+    @Nullable
+    public CharSequence getHeadToHeadTitle() {
+        return mHeadToHeadTitle;
+    }
+
+    @Nullable
+    public SmartspaceIcon getHeadToHeadFirstCompetitorIcon() {
+        return mHeadToHeadFirstCompetitorIcon;
+    }
+
+    @Nullable
+    public SmartspaceIcon getHeadToHeadSecondCompetitorIcon() {
+        return mHeadToHeadSecondCompetitorIcon;
+    }
+
+    @Nullable
+    public CharSequence getHeadToHeadFirstCompetitorText() {
+        return mHeadToHeadFirstCompetitorText;
+    }
+
+    @Nullable
+    public CharSequence getHeadToHeadSecondCompetitorText() {
+        return mHeadToHeadSecondCompetitorText;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getHeadToHeadAction() {
+        return mHeadToHeadAction;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceHeadToHeadUiTemplateData> CREATOR =
+            new Creator<SmartspaceHeadToHeadUiTemplateData>() {
+                @Override
+                public SmartspaceHeadToHeadUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceHeadToHeadUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceHeadToHeadUiTemplateData[] newArray(int size) {
+                    return new SmartspaceHeadToHeadUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        TextUtils.writeToParcel(mHeadToHeadTitle, out, flags);
+        out.writeTypedObject(mHeadToHeadFirstCompetitorIcon, flags);
+        out.writeTypedObject(mHeadToHeadSecondCompetitorIcon, flags);
+        TextUtils.writeToParcel(mHeadToHeadFirstCompetitorText, out, flags);
+        TextUtils.writeToParcel(mHeadToHeadSecondCompetitorText, out, flags);
+        out.writeTypedObject(mHeadToHeadAction, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceHeadToHeadUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceHeadToHeadUiTemplateData that = (SmartspaceHeadToHeadUiTemplateData) o;
+        return SmartspaceUtils.isEqual(mHeadToHeadTitle, that.mHeadToHeadTitle) && Objects.equals(
+                mHeadToHeadFirstCompetitorIcon, that.mHeadToHeadFirstCompetitorIcon)
+                && Objects.equals(
+                mHeadToHeadSecondCompetitorIcon, that.mHeadToHeadSecondCompetitorIcon)
+                && SmartspaceUtils.isEqual(mHeadToHeadFirstCompetitorText,
+                that.mHeadToHeadFirstCompetitorText)
+                && SmartspaceUtils.isEqual(mHeadToHeadSecondCompetitorText,
+                that.mHeadToHeadSecondCompetitorText)
+                && Objects.equals(
+                mHeadToHeadAction, that.mHeadToHeadAction);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mHeadToHeadTitle, mHeadToHeadFirstCompetitorIcon,
+                mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
+                mHeadToHeadSecondCompetitorText,
+                mHeadToHeadAction);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceHeadToHeadUiTemplateData{"
+                + "mH2HTitle=" + mHeadToHeadTitle
+                + ", mH2HFirstCompetitorIcon=" + mHeadToHeadFirstCompetitorIcon
+                + ", mH2HSecondCompetitorIcon=" + mHeadToHeadSecondCompetitorIcon
+                + ", mH2HFirstCompetitorText=" + mHeadToHeadFirstCompetitorText
+                + ", mH2HSecondCompetitorText=" + mHeadToHeadSecondCompetitorText
+                + ", mH2HAction=" + mHeadToHeadAction
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceHeadToHeadUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private CharSequence mHeadToHeadTitle;
+        private SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
+        private SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
+        private CharSequence mHeadToHeadFirstCompetitorText;
+        private CharSequence mHeadToHeadSecondCompetitorText;
+        private SmartspaceTapAction mHeadToHeadAction;
+
+        /**
+         * A builder for {@link SmartspaceHeadToHeadUiTemplateData}.
+         */
+        public Builder() {
+            super(SmartspaceTarget.UI_TEMPLATE_HEAD_TO_HEAD);
+        }
+
+        /**
+         * Sets the head-to-head card's title
+         */
+        @NonNull
+        public Builder setHeadToHeadTitle(@Nullable CharSequence headToHeadTitle) {
+            mHeadToHeadTitle = headToHeadTitle;
+            return this;
+        }
+
+        /**
+         * Sets the head-to-head card's first competitor icon
+         */
+        @NonNull
+        public Builder setHeadToHeadFirstCompetitorIcon(
+                @Nullable SmartspaceIcon headToHeadFirstCompetitorIcon) {
+            mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
+            return this;
+        }
+
+        /**
+         * Sets the head-to-head card's second competitor icon
+         */
+        @NonNull
+        public Builder setHeadToHeadSecondCompetitorIcon(
+                @Nullable SmartspaceIcon headToHeadSecondCompetitorIcon) {
+            mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
+            return this;
+        }
+
+        /**
+         * Sets the head-to-head card's first competitor text
+         */
+        @NonNull
+        public Builder setHeadToHeadFirstCompetitorText(
+                @Nullable CharSequence headToHeadFirstCompetitorText) {
+            mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText;
+            return this;
+        }
+
+        /**
+         * Sets the head-to-head card's second competitor text
+         */
+        @NonNull
+        public Builder setHeadToHeadSecondCompetitorText(
+                @Nullable CharSequence headToHeadSecondCompetitorText) {
+            mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText;
+            return this;
+        }
+
+        /**
+         * Sets the head-to-head card's tap action
+         */
+        @NonNull
+        public Builder setHeadToHeadAction(@Nullable SmartspaceTapAction headToHeadAction) {
+            mHeadToHeadAction = headToHeadAction;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceHeadToHeadUiTemplateData instance.
+         */
+        @NonNull
+        public SmartspaceHeadToHeadUiTemplateData build() {
+            return new SmartspaceHeadToHeadUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    mHeadToHeadTitle,
+                    mHeadToHeadFirstCompetitorIcon,
+                    mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
+                    mHeadToHeadSecondCompetitorText,
+                    mHeadToHeadAction);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
new file mode 100644
index 0000000..70b3095
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceUtils;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds the information for a Smartspace-card icon. Including the icon image itself, and an
+ * optional content description as the icon's accessibility description.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceIcon implements Parcelable {
+
+    @NonNull
+    private final Icon mIcon;
+
+    @Nullable
+    private final CharSequence mContentDescription;
+
+    SmartspaceIcon(@NonNull Parcel in) {
+        mIcon = in.readTypedObject(Icon.CREATOR);
+        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+    }
+
+    private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription) {
+        mIcon = icon;
+        mContentDescription = contentDescription;
+    }
+
+    @NonNull
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    @Nullable
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    @NonNull
+    public static final Creator<SmartspaceIcon> CREATOR = new Creator<SmartspaceIcon>() {
+        @Override
+        public SmartspaceIcon createFromParcel(Parcel in) {
+            return new SmartspaceIcon(in);
+        }
+
+        @Override
+        public SmartspaceIcon[] newArray(int size) {
+            return new SmartspaceIcon[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceIcon)) return false;
+        SmartspaceIcon that = (SmartspaceIcon) o;
+        return mIcon.equals(that.mIcon) && SmartspaceUtils.isEqual(mContentDescription,
+                that.mContentDescription);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIcon, mContentDescription);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeTypedObject(mIcon, flags);
+        TextUtils.writeToParcel(mContentDescription, out, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceIcon{"
+                + "mImage=" + mIcon
+                + ", mContentDescription='" + mContentDescription + '\''
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceIcon} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+
+        private Icon mIcon;
+        private CharSequence mContentDescription;
+
+        /**
+         * A builder for {@link SmartspaceIcon}.
+         *
+         * @param icon the icon image of this smartspace icon.
+         */
+        public Builder(@NonNull Icon icon) {
+            mIcon = Objects.requireNonNull(icon);
+        }
+
+        /**
+         * Sets the icon's content description.
+         */
+        @NonNull
+        public Builder setContentDescription(@NonNull CharSequence contentDescription) {
+            mContentDescription = contentDescription;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceIcon instance.
+         */
+        @NonNull
+        public SmartspaceIcon build() {
+            return new SmartspaceIcon(mIcon, mContentDescription);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
new file mode 100644
index 0000000..287cf8e
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
@@ -0,0 +1,198 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-card Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    /** Icon for the sub-card. */
+    @NonNull
+    private final SmartspaceIcon mSubCardIcon;
+
+    /** Text for the sub-card, which shows below the icon when being set. */
+    @Nullable
+    private final CharSequence mSubCardText;
+
+    /** Tap action for the sub-card secondary card. */
+    @Nullable
+    private final SmartspaceTapAction mSubCardAction;
+
+    SmartspaceSubCardUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mSubCardIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mSubCardText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSubCardAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+    }
+
+    private SmartspaceSubCardUiTemplateData(int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @NonNull SmartspaceIcon subCardIcon,
+            @Nullable CharSequence subCardText,
+            @Nullable SmartspaceTapAction subCardAction) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mSubCardIcon = subCardIcon;
+        mSubCardText = subCardText;
+        mSubCardAction = subCardAction;
+    }
+
+    @NonNull
+    public SmartspaceIcon getSubCardIcon() {
+        return mSubCardIcon;
+    }
+
+    @Nullable
+    public CharSequence getSubCardText() {
+        return mSubCardText;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getSubCardAction() {
+        return mSubCardAction;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceSubCardUiTemplateData> CREATOR =
+            new Creator<SmartspaceSubCardUiTemplateData>() {
+                @Override
+                public SmartspaceSubCardUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceSubCardUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceSubCardUiTemplateData[] newArray(int size) {
+                    return new SmartspaceSubCardUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeTypedObject(mSubCardIcon, flags);
+        TextUtils.writeToParcel(mSubCardText, out, flags);
+        out.writeTypedObject(mSubCardAction, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceSubCardUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceSubCardUiTemplateData that = (SmartspaceSubCardUiTemplateData) o;
+        return mSubCardIcon.equals(that.mSubCardIcon) && SmartspaceUtils.isEqual(mSubCardText,
+                that.mSubCardText) && Objects.equals(mSubCardAction,
+                that.mSubCardAction);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mSubCardIcon, mSubCardText, mSubCardAction);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceSubCardUiTemplateData{"
+                + "mSubCardIcon=" + mSubCardIcon
+                + ", mSubCardText=" + mSubCardText
+                + ", mSubCardAction=" + mSubCardAction
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceSubCardUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private final SmartspaceIcon mSubCardIcon;
+        private CharSequence mSubCardText;
+        private SmartspaceTapAction mSubCardAction;
+
+        /**
+         * A builder for {@link SmartspaceSubCardUiTemplateData}.
+         */
+        public Builder(@NonNull SmartspaceIcon subCardIcon) {
+            super(SmartspaceTarget.UI_TEMPLATE_SUB_CARD);
+            mSubCardIcon = Objects.requireNonNull(subCardIcon);
+        }
+
+        /**
+         * Sets the card title text.
+         */
+        @NonNull
+        public Builder setSubCardAction(@NonNull CharSequence subCardTitleText) {
+            mSubCardText = subCardTitleText;
+            return this;
+        }
+
+        /**
+         * Sets the card tap action.
+         */
+        @NonNull
+        public Builder setSubCardAction(@NonNull SmartspaceTapAction subCardAction) {
+            mSubCardAction = subCardAction;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceSubCardUiTemplateData instance.
+         */
+        @NonNull
+        public SmartspaceSubCardUiTemplateData build() {
+            return new SmartspaceSubCardUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon,
+                    mSubCardText,
+                    mSubCardAction);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
new file mode 100644
index 0000000..c479993
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
@@ -0,0 +1,192 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-image Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    /** Texts are shown next to the image as a vertical list */
+    @NonNull
+    private final List<CharSequence> mSubImageTexts;
+
+    /** If multiple images are passed in, they will be rendered as GIF. */
+    @NonNull
+    private final List<SmartspaceIcon> mSubImages;
+
+    /** Tap action for the sub-image secondary card. */
+    @Nullable
+    private final SmartspaceTapAction mSubImageAction;
+
+    SmartspaceSubImageUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mSubImageTexts = Arrays.asList(in.readCharSequenceArray());
+        mSubImages = in.createTypedArrayList(SmartspaceIcon.CREATOR);
+        mSubImageAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+    }
+
+    private SmartspaceSubImageUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @NonNull List<CharSequence> subImageTexts,
+            @NonNull List<SmartspaceIcon> subImages,
+            @Nullable SmartspaceTapAction subImageAction) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mSubImageTexts = subImageTexts;
+        mSubImages = subImages;
+        mSubImageAction = subImageAction;
+    }
+
+    @NonNull
+    public List<CharSequence> getSubImageTexts() {
+        return mSubImageTexts;
+    }
+
+    @NonNull
+    public List<SmartspaceIcon> getSubImages() {
+        return mSubImages;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getSubImageAction() {
+        return mSubImageAction;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceSubImageUiTemplateData> CREATOR =
+            new Creator<SmartspaceSubImageUiTemplateData>() {
+                @Override
+                public SmartspaceSubImageUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceSubImageUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceSubImageUiTemplateData[] newArray(int size) {
+                    return new SmartspaceSubImageUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeCharSequenceList(new ArrayList<>(mSubImageTexts));
+        out.writeTypedList(mSubImages);
+        out.writeTypedObject(mSubImageAction, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceSubImageUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceSubImageUiTemplateData that = (SmartspaceSubImageUiTemplateData) o;
+        return Objects.equals(mSubImageTexts, that.mSubImageTexts)
+                && Objects.equals(mSubImages, that.mSubImages) && Objects.equals(
+                mSubImageAction, that.mSubImageAction);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mSubImageTexts, mSubImages, mSubImageAction);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceSubImageUiTemplateData{"
+                + "mSubImageTexts=" + mSubImageTexts
+                + ", mSubImages=" + mSubImages
+                + ", mSubImageAction=" + mSubImageAction
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceSubImageUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private final List<CharSequence> mSubImageTexts;
+        private final List<SmartspaceIcon> mSubImages;
+        private SmartspaceTapAction mSubImageAction;
+
+        /**
+         * A builder for {@link SmartspaceSubImageUiTemplateData}.
+         */
+        public Builder(@NonNull List<CharSequence> subImageTexts,
+                @NonNull List<SmartspaceIcon> subImages) {
+            super(SmartspaceTarget.UI_TEMPLATE_SUB_IMAGE);
+            mSubImageTexts = Objects.requireNonNull(subImageTexts);
+            mSubImages = Objects.requireNonNull(subImages);
+        }
+
+        /**
+         * Sets the card tap action.
+         */
+        @NonNull
+        public Builder setCarouselAction(@NonNull SmartspaceTapAction subImageAction) {
+            mSubImageAction = subImageAction;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceSubImageUiTemplateData instance.
+         */
+        @NonNull
+        public SmartspaceSubImageUiTemplateData build() {
+            return new SmartspaceSubImageUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts,
+                    mSubImages,
+                    mSubImageAction);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
new file mode 100644
index 0000000..b5d9645
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
@@ -0,0 +1,196 @@
+/*
+ * 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.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceTarget;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Holds all the relevant data needed to render a Smartspace card with the sub-list Ui Template.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTemplateData {
+
+    @Nullable
+    private final SmartspaceIcon mSubListIcon;
+    @NonNull
+    private final List<CharSequence> mSubListTexts;
+
+    /** Tap action for the sub-list secondary card. */
+    @Nullable
+    private final SmartspaceTapAction mSubListAction;
+
+    SmartspaceSubListUiTemplateData(@NonNull Parcel in) {
+        super(in);
+        mSubListIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+        mSubListTexts = Arrays.asList(in.readCharSequenceArray());
+        mSubListAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
+    }
+
+    private SmartspaceSubListUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
+            @Nullable CharSequence titleText,
+            @Nullable SmartspaceIcon titleIcon,
+            @Nullable CharSequence subtitleText,
+            @Nullable SmartspaceIcon subTitleIcon,
+            @Nullable SmartspaceTapAction primaryTapAction,
+            @Nullable CharSequence supplementalSubtitleText,
+            @Nullable SmartspaceIcon supplementalSubtitleIcon,
+            @Nullable SmartspaceTapAction supplementalSubtitleTapAction,
+            @Nullable CharSequence supplementalAlarmText,
+            @Nullable SmartspaceIcon subListIcon,
+            @NonNull List<CharSequence> subListTexts,
+            @Nullable SmartspaceTapAction subListAction) {
+        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
+                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
+                supplementalAlarmText);
+        mSubListIcon = subListIcon;
+        mSubListTexts = subListTexts;
+        mSubListAction = subListAction;
+    }
+
+    @Nullable
+    public SmartspaceIcon getSubListIcon() {
+        return mSubListIcon;
+    }
+
+    @NonNull
+    public List<CharSequence> getSubListTexts() {
+        return mSubListTexts;
+    }
+
+    @Nullable
+    public SmartspaceTapAction getSubListAction() {
+        return mSubListAction;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceSubListUiTemplateData> CREATOR =
+            new Creator<SmartspaceSubListUiTemplateData>() {
+                @Override
+                public SmartspaceSubListUiTemplateData createFromParcel(Parcel in) {
+                    return new SmartspaceSubListUiTemplateData(in);
+                }
+
+                @Override
+                public SmartspaceSubListUiTemplateData[] newArray(int size) {
+                    return new SmartspaceSubListUiTemplateData[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeTypedObject(mSubListIcon, flags);
+        out.writeCharSequenceList(new ArrayList<>(mSubListTexts));
+        out.writeTypedObject(mSubListAction, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceSubListUiTemplateData)) return false;
+        if (!super.equals(o)) return false;
+        SmartspaceSubListUiTemplateData that = (SmartspaceSubListUiTemplateData) o;
+        return Objects.equals(mSubListIcon, that.mSubListIcon) && Objects.equals(
+                mSubListTexts, that.mSubListTexts) && Objects.equals(mSubListAction,
+                that.mSubListAction);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mSubListIcon, mSubListTexts, mSubListAction);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " + SmartspaceSubListUiTemplateData{"
+                + "mSubListIcon=" + mSubListIcon
+                + ", mSubListTexts=" + mSubListTexts
+                + ", mSubListAction=" + mSubListAction
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceSubListUiTemplateData} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
+
+        private SmartspaceIcon mSubListIcon;
+        private final List<CharSequence> mSubListTexts;
+        private SmartspaceTapAction mSubListAction;
+
+        /**
+         * A builder for {@link SmartspaceSubListUiTemplateData}.
+         */
+        public Builder(@NonNull List<CharSequence> subListTexts) {
+            super(SmartspaceTarget.UI_TEMPLATE_SUB_LIST);
+            mSubListTexts = Objects.requireNonNull(subListTexts);
+        }
+
+        /**
+         * Sets the sub-list card icon.
+         */
+        @NonNull
+        public Builder setSubListIcon(@NonNull SmartspaceIcon subListIcon) {
+            mSubListIcon = subListIcon;
+            return this;
+        }
+
+        /**
+         * Sets the card tap action.
+         */
+        @NonNull
+        public Builder setCarouselAction(@NonNull SmartspaceTapAction subListAction) {
+            mSubListAction = subListAction;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceSubListUiTemplateData instance.
+         */
+        @NonNull
+        public SmartspaceSubListUiTemplateData build() {
+            return new SmartspaceSubListUiTemplateData(getTemplateType(), getTitleText(),
+                    getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+                    getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
+                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon,
+                    mSubListTexts,
+                    mSubListAction);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java
new file mode 100644
index 0000000..27d8e5f
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceTapAction.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.smartspace.uitemplatedata;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.app.smartspace.SmartspaceUtils;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceTapAction} represents an action which can be taken by a user by tapping on
+ * either the title, the subtitle or on the icon. Supported instances are Intents and
+ * PendingIntents. These actions can be called from another process or within the client process.
+ *
+ * Clients can also receive ShorcutInfos in the extras bundle.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTapAction implements Parcelable {
+
+    /** A unique Id of this {@link SmartspaceTapAction}. */
+    @Nullable
+    private final CharSequence mId;
+
+    @Nullable
+    private final Intent mIntent;
+
+    @Nullable
+    private final PendingIntent mPendingIntent;
+
+    @Nullable
+    private final UserHandle mUserHandle;
+
+    @Nullable
+    private Bundle mExtras;
+
+    SmartspaceTapAction(@NonNull Parcel in) {
+        mId = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mIntent = in.readTypedObject(Intent.CREATOR);
+        mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+        mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+        mExtras = in.readBundle();
+    }
+
+    private SmartspaceTapAction(@Nullable CharSequence id, @Nullable Intent intent,
+            @Nullable PendingIntent pendingIntent, @Nullable UserHandle userHandle,
+            @Nullable Bundle extras) {
+        mId = id;
+        mIntent = intent;
+        mPendingIntent = pendingIntent;
+        mUserHandle = userHandle;
+        mExtras = extras;
+    }
+
+    @Nullable
+    public CharSequence getId() {
+        return mId;
+    }
+
+    @SuppressLint("IntentBuilderName")
+    @Nullable
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    @Nullable
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    @Nullable
+    public UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    @Nullable
+    @SuppressLint("NullableCollection")
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        TextUtils.writeToParcel(mId, out, flags);
+        out.writeTypedObject(mIntent, flags);
+        out.writeTypedObject(mPendingIntent, flags);
+        out.writeTypedObject(mUserHandle, flags);
+        out.writeBundle(mExtras);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<SmartspaceTapAction> CREATOR = new Creator<SmartspaceTapAction>() {
+        @Override
+        public SmartspaceTapAction createFromParcel(Parcel in) {
+            return new SmartspaceTapAction(in);
+        }
+
+        @Override
+        public SmartspaceTapAction[] newArray(int size) {
+            return new SmartspaceTapAction[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceTapAction)) return false;
+        SmartspaceTapAction that = (SmartspaceTapAction) o;
+        return SmartspaceUtils.isEqual(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceTapAction{"
+                + "mId=" + mId
+                + "mIntent=" + mIntent
+                + ", mPendingIntent=" + mPendingIntent
+                + ", mUserHandle=" + mUserHandle
+                + ", mExtras=" + mExtras
+                + '}';
+    }
+
+    /**
+     * A builder for {@link SmartspaceTapAction} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+
+        private CharSequence mId;
+        private Intent mIntent;
+        private PendingIntent mPendingIntent;
+        private UserHandle mUserHandle;
+        private Bundle mExtras;
+
+        /**
+         * A builder for {@link SmartspaceTapAction}.
+         *
+         * @param id A unique Id of this {@link SmartspaceTapAction}.
+         */
+        public Builder(@NonNull CharSequence id) {
+            mId = Objects.requireNonNull(id);
+        }
+
+        /**
+         * Sets the action intent.
+         */
+        @NonNull
+        public Builder setIntent(@NonNull Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Sets the pending intent.
+         */
+        @NonNull
+        public Builder setPendingIntent(@NonNull PendingIntent pendingIntent) {
+            mPendingIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * Sets the user handle.
+         */
+        @NonNull
+        @SuppressLint("UserHandleName")
+        public Builder setUserHandle(@Nullable UserHandle userHandle) {
+            mUserHandle = userHandle;
+            return this;
+        }
+
+        /**
+         * Sets the extras.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceTapAction instance.
+         *
+         * @throws IllegalStateException if the tap action is empty.
+         */
+        @NonNull
+        public SmartspaceTapAction build() {
+            if (mIntent == null && mPendingIntent == null && mExtras == null) {
+                throw new IllegalStateException("Please assign at least 1 valid tap field");
+            }
+            return new SmartspaceTapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras);
+        }
+    }
+}
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index a36da88..6e3bbcc 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -24,7 +24,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * This class provides an API surface for system apps to manipulate the app hibernation
@@ -129,4 +132,38 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Returns the stats from app hibernation for each package provided.
+     *
+     * @param packageNames the set of packages to return stats for
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+    public @NonNull Map<String, HibernationStats> getHibernationStatsForUser(
+            @NonNull Set<String> packageNames) {
+        try {
+            return mIAppHibernationService.getHibernationStatsForUser(
+                    new ArrayList(packageNames), mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the stats from app hibernation for all packages for the user
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+    public @NonNull Map<String, HibernationStats> getHibernationStatsForUser() {
+        try {
+            return mIAppHibernationService.getHibernationStatsForUser(
+                    null /* packageNames */, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/core/java/android/apphibernation/HibernationStats.aidl
similarity index 72%
rename from core/java/android/view/selectiontoolbar/SelectionContext.aidl
rename to core/java/android/apphibernation/HibernationStats.aidl
index 5206831..a92b903 100644
--- a/core/java/android/view/selectiontoolbar/SelectionContext.aidl
+++ b/core/java/android/apphibernation/HibernationStats.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
+/**
+ * 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
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-package android.view.selectiontoolbar;
+package android.apphibernation;
 
-/**
- * @hide
- */
-parcelable SelectionContext;
+parcelable HibernationStats;
\ No newline at end of file
diff --git a/core/java/android/apphibernation/HibernationStats.java b/core/java/android/apphibernation/HibernationStats.java
new file mode 100644
index 0000000..2c4db82
--- /dev/null
+++ b/core/java/android/apphibernation/HibernationStats.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 android.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stats for a hibernating package.
+ * @hide
+ */
+@SystemApi
+public final class HibernationStats implements Parcelable {
+    private final long mDiskBytesSaved;
+
+    /** @hide */
+    public HibernationStats(long diskBytesSaved) {
+        mDiskBytesSaved = diskBytesSaved;
+    }
+
+    private HibernationStats(@NonNull Parcel in) {
+        mDiskBytesSaved = in.readLong();
+    }
+
+    /**
+     * Get the disk storage saved from hibernation in bytes.
+     */
+    public long getDiskBytesSaved() {
+        return mDiskBytesSaved;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mDiskBytesSaved);
+    }
+
+    public static final @NonNull Creator<HibernationStats> CREATOR =
+            new Creator<HibernationStats>() {
+        @Override
+        public HibernationStats createFromParcel(Parcel in) {
+            return new HibernationStats(in);
+        }
+
+        @Override
+        public HibernationStats[] newArray(int size) {
+            return new HibernationStats[size];
+        }
+    };
+}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index afdb3fe..11bb6b5 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -16,6 +16,8 @@
 
 package android.apphibernation;
 
+import android.apphibernation.HibernationStats;
+
 /**
  * Binder interface to communicate with AppHibernationService.
  * @hide
@@ -26,4 +28,6 @@
     boolean isHibernatingGlobally(String packageName);
     void setHibernatingGlobally(String packageName, boolean isHibernating);
     List<String> getHibernatingPackagesForUser(int userId);
+    Map<String, HibernationStats> getHibernationStatsForUser(in List<String> packageNames,
+            int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b8b96b7..4b4e008 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5107,6 +5107,16 @@
     public static final String DROPBOX_SERVICE = "dropbox";
 
     /**
+     * System service name for BinaryTransparencyService. This is used to retrieve measurements
+     * pertaining to various pre-installed and system binaries on device for the purposes of
+     * providing transparency to the user.
+     *
+     * @hide
+     */
+    @SuppressLint("ServiceName")
+    public static final String BINARY_TRANSPARENCY_SERVICE = "transparency";
+
+    /**
      * System service name for the DeviceIdleManager.
      * @see #getSystemService(String)
      * @hide
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1f83207..fb186fd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3801,47 +3801,6 @@
             "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
 
     /**
-     * Broadcast Action: A broadcast sent by the system to indicate that
-     * {@link android.safetycenter.SafetyCenterManager} is requesting data from safety sources
-     * regarding their safety state.
-     *
-     * This broadcast is sent when a user triggers a data refresh from the Safety Center UI or when
-     * Safety Center detects that its stored safety information is stale and needs to be updated.
-     *
-     * This broadcast is sent explicitly to safety sources by targeting intents to a specified set
-     * of components provided by the safety sources in the safety source configuration.
-     * The receiving components should be manifest-declared receivers so that safety sources can be
-     * requested to send data even if they are not running.
-     *
-     * On receiving this broadcast, safety sources should determine their safety state
-     * according to the parameters specified in the intent extras (see below) and send Safety Center
-     * data about their safety state using
-     * {@link android.safetycenter.SafetyCenterManager#sendSafetyCenterUpdate(android.safetycenter.SafetySourceData)}.
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     *
-     * <p>Includes the following extras:
-     * <ul>
-     * <li>{@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}: An int representing the type of data
-     * being requested. Possible values are all values in {@link RefreshRequestType}.
-     * <li>{@link #EXTRA_REFRESH_SAFETY_SOURCE_IDS}: A {@code String[]} of ids
-     * representing the safety sources being requested for data. This extra exists for
-     * disambiguation in the case that a single component is responsible for receiving refresh
-     * requests for multiple safety sources.
-     * </ul>
-     *
-     * @hide
-     */
-    // TODO(b/210805082): Define the term "safety sources" more concretely here once safety sources
-    //  are configured in xml config.
-    // TODO(b/210979035): Determine recommendation for sources if they are requested for fresh data
-    //  but cannot provide it.
-    @SystemApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_REFRESH_SAFETY_SOURCES =
-            "android.intent.action.REFRESH_SAFETY_SOURCES";
-
-    /**
      * Broadcast Action: a remote intent is to be broadcasted.
      *
      * A remote intent is used for remote RPC between devices. The remote intent
@@ -6476,77 +6435,6 @@
     public static final String EXTRA_VISIBILITY_ALLOW_LIST =
             "android.intent.extra.VISIBILITY_ALLOW_LIST";
 
-
-    /**
-     * Used as a {@code String[]} extra field in
-     * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} intents to specify the safety
-     * source ids of the safety sources being requested for data by Safety Center.
-     *
-     * When this extra field is not specified in the intent, it is assumed that Safety Center is
-     * requesting data from all safety sources supported by the component receiving the broadcast.
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS =
-            "android.intent.extra.REFRESH_SAFETY_SOURCE_IDS";
-
-    /**
-     * Used as an {@code int} extra field in
-     * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} intents to specify the type of
-     * data request from Safety Center.
-     *
-     * Possible values are all values in {@link RefreshRequestType}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE =
-            "android.intent.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE";
-
-    /**
-     * All possible types of data refresh requests in broadcasts with intent action
-     * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}.
-     *
-     * @hide
-     */
-    @IntDef(prefix = { "EXTRA_REFRESH_REQUEST_TYPE_" }, value = {
-            EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA,
-            EXTRA_REFRESH_REQUEST_TYPE_GET_DATA,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RefreshRequestType {}
-
-    /**
-     * Used as an int value for
-     * {@link android.content.Intent#EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}
-     * to indicate that the safety source should fetch fresh data relating to their safety state
-     * upon receiving a broadcast with intent action
-     * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES} and provide it to Safety Center.
-     *
-     * The term "fresh" here means that the sources should ensure that the safety data is accurate
-     * as possible at the time of providing it to Safety Center, even if it involves performing an
-     * expensive and/or slow process.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA = 0;
-
-    /**
-     * Used as an int value for
-     * {@link android.content.Intent#EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE}
-     * to indicate that upon receiving a broadcasts with intent action
-     * {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}, the safety source should
-     * provide data relating to their safety state to Safety Center.
-     *
-     * If the source already has its safety data cached, it may provide it without triggering a
-     * process to fetch state which may be expensive and/or slow.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EXTRA_REFRESH_REQUEST_TYPE_GET_DATA = 1;
-
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5b0c275..e5c31d7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3154,6 +3154,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports exposing head tracker sensors from peripheral
+     * devices via the dynamic sensors API.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_DYNAMIC_HEAD_TRACKER = "android.hardware.sensor.dynamic.head_tracker";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports high fidelity sensor processing
      * capabilities.
      */
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index a5d97f9..bb88486 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -363,6 +363,9 @@
     /**
      * The group this permission is a part of, as per
      * {@link android.R.attr#permissionGroup}.
+     * <p>
+     * The actual grouping of platform-defined runtime permissions is subject to change and can be
+     * queried with {@link PackageManager#getGroupOfPlatformPermission}.
      */
     public @Nullable String group;
 
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 1aba961..5fd0d84 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -83,7 +83,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Class for accessing an application's resources.  This sits on top of the
@@ -143,9 +142,6 @@
     @UnsupportedAppUsage
     private DrawableInflater mDrawableInflater;
 
-    /** Used to override the returned adjustments of {@link #getDisplayAdjustments}. */
-    private DisplayAdjustments mOverrideDisplayAdjustments;
-
     /** Lock object used to protect access to {@link #mTmpValue}. */
     private final Object mTmpValueLock = new Object();
 
@@ -2174,38 +2170,15 @@
     /** @hide */
     @UnsupportedAppUsage(trackingBug = 176190631)
     public DisplayAdjustments getDisplayAdjustments() {
-        final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments;
-        if (overrideDisplayAdjustments != null) {
-            return overrideDisplayAdjustments;
-        }
         return mResourcesImpl.getDisplayAdjustments();
     }
 
     /**
-     * Customize the display adjustments based on the current one in {@link #mResourcesImpl}, in
-     * order to isolate the effect with other instances of {@link Resource} that may share the same
-     * instance of {@link ResourcesImpl}.
-     *
-     * @param override The operation to override the existing display adjustments. If it is null,
-     *                 the override adjustments will be cleared.
-     * @hide
-     */
-    public void overrideDisplayAdjustments(@Nullable Consumer<DisplayAdjustments> override) {
-        if (override != null) {
-            mOverrideDisplayAdjustments = new DisplayAdjustments(
-                    mResourcesImpl.getDisplayAdjustments());
-            override.accept(mOverrideDisplayAdjustments);
-        } else {
-            mOverrideDisplayAdjustments = null;
-        }
-    }
-
-    /**
      * Return {@code true} if the override display adjustments have been set.
      * @hide
      */
     public boolean hasOverrideDisplayAdjustments() {
-        return mOverrideDisplayAdjustments != null;
+        return false;
     }
 
     /**
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 7074a2c..79153d7 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -31,6 +31,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.service.SensorPrivacyIndividualEnabledSensorProto;
+import android.service.SensorPrivacySensorProto;
 import android.service.SensorPrivacyToggleSourceProto;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -75,7 +76,7 @@
     private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
 
     /**
-     * Individual sensors not listed in {@link Sensors}
+     * Sensor constants which are used in {@link SensorPrivacyManager}
      */
     public static class Sensors {
 
@@ -84,12 +85,12 @@
         /**
          * Constant for the microphone
          */
-        public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+        public static final int MICROPHONE = SensorPrivacySensorProto.MICROPHONE;
 
         /**
          * Constant for the camera
          */
-        public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+        public static final int CAMERA = SensorPrivacySensorProto.CAMERA;
 
         /**
          * Individual sensors not listed in {@link Sensors}
@@ -161,6 +162,68 @@
     }
 
     /**
+     * Types of toggles which can exist for sensor privacy
+     * @hide
+     */
+    public static class ToggleTypes {
+        private ToggleTypes() {}
+
+        /**
+         * Constant for software toggle.
+         */
+        public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+        /**
+         * Constant for hardware toggle.
+         */
+        public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
+        /**
+         * Types of toggles which can exist for sensor privacy
+         *
+         * @hide
+         */
+        @IntDef(value = {
+                SOFTWARE,
+                HARDWARE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ToggleType {}
+
+    }
+
+    /**
+     * Types of state which can exist for the sensor privacy toggle
+     * @hide
+     */
+    public static class StateTypes {
+        private StateTypes() {}
+
+        /**
+         * Constant indicating privacy is enabled.
+         */
+        public static final int ENABLED = SensorPrivacyIndividualEnabledSensorProto.ENABLED;
+
+        /**
+         * Constant indicating privacy is disabled.
+         */
+        public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED;
+
+        /**
+         * Types of state which can exist for a sensor privacy toggle
+         *
+         * @hide
+         */
+        @IntDef(value = {
+                ENABLED,
+                DISABLED
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface StateType {}
+
+    }
+
+    /**
      * A class implementing this interface can register with the {@link
      * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
      * state changes.
@@ -507,7 +570,6 @@
     /**
      * Don't show dialogs to turn off sensor privacy for this package.
      *
-     * @param packageName Package name not to show dialogs for
      * @param suppress Whether to suppress or re-enable.
      *
      * @hide
@@ -521,7 +583,6 @@
     /**
      * Don't show dialogs to turn off sensor privacy for this package.
      *
-     * @param packageName Package name not to show dialogs for
      * @param suppress Whether to suppress or re-enable.
      * @param userId the user's id
      *
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 0dc8f92..99f3d15 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -57,6 +57,23 @@
      */
     public static final int HIGH_BRIGHTNESS_MODE_HDR = 2;
 
+    @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
+            BRIGHTNESS_MAX_REASON_NONE,
+            BRIGHTNESS_MAX_REASON_THERMAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BrightnessMaxReason {}
+
+    /**
+     * Maximum brightness is unrestricted.
+     */
+    public static final int BRIGHTNESS_MAX_REASON_NONE = 0;
+
+    /**
+     * Maximum brightness is restricted due to thermal throttling.
+     */
+    public static final int BRIGHTNESS_MAX_REASON_THERMAL = 1;
+
     /** Brightness */
     public final float brightness;
 
@@ -78,21 +95,29 @@
      */
     public final int highBrightnessMode;
 
+    /**
+     * The current reason for restricting maximum brightness.
+     * Can be any of BRIGHTNESS_MAX_REASON_* values.
+     */
+    public final int brightnessMaxReason;
+
     public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
-            @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) {
+            @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint,
+            @BrightnessMaxReason int brightnessMaxReason) {
         this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode,
-                highBrightnessTransitionPoint);
+                highBrightnessTransitionPoint, brightnessMaxReason);
     }
 
     public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
             float brightnessMaximum, @HighBrightnessMode int highBrightnessMode,
-            float highBrightnessTransitionPoint) {
+            float highBrightnessTransitionPoint, @BrightnessMaxReason int brightnessMaxReason) {
         this.brightness = brightness;
         this.adjustedBrightness = adjustedBrightness;
         this.brightnessMinimum = brightnessMinimum;
         this.brightnessMaximum = brightnessMaximum;
         this.highBrightnessMode = highBrightnessMode;
         this.highBrightnessTransitionPoint = highBrightnessTransitionPoint;
+        this.brightnessMaxReason =  brightnessMaxReason;
     }
 
     /**
@@ -110,6 +135,19 @@
         return "invalid";
     }
 
+    /**
+     * @return User-friendly string for specified {@link BrightnessMaxReason} parameter.
+     */
+    public static String briMaxReasonToString(@BrightnessMaxReason int reason) {
+        switch (reason) {
+            case BRIGHTNESS_MAX_REASON_NONE:
+                return "none";
+            case BRIGHTNESS_MAX_REASON_THERMAL:
+                return "thermal";
+        }
+        return "invalid";
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -123,6 +161,7 @@
         dest.writeFloat(brightnessMaximum);
         dest.writeInt(highBrightnessMode);
         dest.writeFloat(highBrightnessTransitionPoint);
+        dest.writeInt(brightnessMaxReason);
     }
 
     public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
@@ -145,6 +184,7 @@
         brightnessMaximum = source.readFloat();
         highBrightnessMode = source.readInt();
         highBrightnessTransitionPoint = source.readFloat();
+        brightnessMaxReason = source.readInt();
     }
 
 }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 4432caf..41642e7 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -78,6 +78,7 @@
     private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
     private static final int DO_CAN_START_STYLUS_HANDWRITING = 100;
     private static final int DO_START_STYLUS_HANDWRITING = 110;
+    private static final int DO_INIT_INK_WINDOW = 120;
 
     final WeakReference<InputMethodServiceInternal> mTarget;
     final Context mContext;
@@ -250,6 +251,10 @@
                 args.recycle();
                 return;
             }
+            case DO_INIT_INK_WINDOW: {
+                inputMethod.initInkWindow();
+                return;
+            }
 
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
@@ -400,4 +405,10 @@
                 mCaller.obtainMessageIOO(DO_START_STYLUS_HANDWRITING, requestId, channel,
                         stylusEvents));
     }
+
+    @BinderThread
+    @Override
+    public void initInkWindow() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_INIT_INK_WINDOW));
+    }
 }
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index f8d2fe2..83b053a 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -59,15 +59,26 @@
     }
 
     /**
+     * Initialize InkWindow if we only want to create and draw surface but not show it.
+     */
+    void initOnly() {
+        show(true /* keepInvisible */);
+    }
+
+    /**
      * Method to show InkWindow on screen.
      * Emulates internal behavior similar to Dialog.show().
      */
     void show() {
+        show(false /* keepInvisible */);
+    }
+
+    private void show(boolean keepInvisible) {
         if (getDecorView() == null) {
             Slog.i(InputMethodService.TAG, "DecorView is not set for InkWindow. show() failed.");
             return;
         }
-        getDecorView().setVisibility(View.VISIBLE);
+        getDecorView().setVisibility(keepInvisible ? View.INVISIBLE : View.VISIBLE);
         if (!mIsViewAdded) {
             mWindowManager.addView(getDecorView(), getAttributes());
             mIsViewAdded = true;
@@ -91,4 +102,11 @@
         lp.token = token;
         setAttributes(lp);
     }
+
+    /**
+     * Returns {@code true} if Window was created and added to WM.
+     */
+    boolean isInitialized() {
+        return mIsViewAdded;
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 80da361..14f92fb 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -475,7 +475,7 @@
     private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
 
     @NonNull
-    private final NavigationBarController mNavigationBarController =
+    final NavigationBarController mNavigationBarController =
             new NavigationBarController(this);
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -742,6 +742,10 @@
             onUnbindInput();
             mInputBinding = null;
             mInputConnection = null;
+            // free-up cached InkWindow surface on detaching from current client.
+            if (mInkWindow != null) {
+                mInkWindow.hide(true /* remove */);
+            }
         }
 
         /**
@@ -902,6 +906,10 @@
                 Log.d(TAG, "Input should have started before starting Stylus handwriting.");
                 return;
             }
+            if (!mInkWindow.isInitialized()) {
+                // prepare hasn't been called by Stylus HOVER.
+                onPrepareStylusHandwriting();
+            }
             if (onStartStylusHandwriting()) {
                 mPrivOps.onStylusHandwritingReady(requestId);
             } else {
@@ -949,6 +957,16 @@
 
         /**
          * {@inheritDoc}
+         * @hide
+         */
+        @Override
+        public void initInkWindow() {
+            mInkWindow.initOnly();
+            onPrepareStylusHandwriting();
+        }
+
+        /**
+         * {@inheritDoc}
          */
         @MainThread
         @Override
@@ -2198,10 +2216,12 @@
     }
     
     /**
-     * Called by the framework to create the layout for showing extacted text.
+     * Called by the framework to create the layout for showing extracted text.
      * Only called when in fullscreen mode.  The returned view hierarchy must
      * have an {@link ExtractEditText} whose ID is 
-     * {@link android.R.id#inputExtractEditText}.
+     * {@link android.R.id#inputExtractEditText}, with action ID
+     * {@link android.R.id#inputExtractAction} and accessories ID
+     * {@link android.R.id#inputExtractAccessories}.
      */
     public View onCreateExtractTextView() {
         return mInflater.inflate(
@@ -2318,7 +2338,19 @@
             }
         }
     }
-    
+
+    /**
+     * Called to prepare stylus handwriting.
+     * The system calls this before the first {@link #onStartStylusHandwriting} request.
+     *
+     * <p>Note: The system tries to call this as early as possible, when it detects that
+     * handwriting stylus input is imminent. However, that a subsequent call to
+     * {@link #onStartStylusHandwriting} actually happens is not guaranteed.</p>
+     */
+    public void onPrepareStylusHandwriting() {
+        // Intentionally empty
+    }
+
     /**
      * Called when an app requests stylus handwriting
      * {@link InputMethodManager#startStylusHandwriting(View)}.
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index a6e475a..2484cf0 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -17,7 +17,9 @@
 package android.inputmethodservice;
 
 import static android.content.Intent.ACTION_OVERLAY_CHANGED;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.StatusBarManager;
@@ -40,6 +42,7 @@
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowInsets;
+import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManagerPolicyConstants;
 import android.widget.FrameLayout;
 
@@ -68,6 +71,9 @@
         default void onDestroy() {
         }
 
+        default void onSystemBarAppearanceChanged(@Appearance int appearance) {
+        }
+
         default String toDebugString() {
             return "No-op implementation";
         }
@@ -100,6 +106,10 @@
         mImpl.onDestroy();
     }
 
+    void onSystemBarAppearanceChanged(@Appearance int appearance) {
+        mImpl.onSystemBarAppearanceChanged(appearance);
+    }
+
     String toDebugString() {
         return mImpl.toDebugString();
     }
@@ -120,6 +130,9 @@
         @Nullable
         private BroadcastReceiver mSystemOverlayChangedReceiver;
 
+        @Appearance
+        private int mAppearance;
+
         Impl(@NonNull InputMethodService inputMethodService) {
             mService = inputMethodService;
         }
@@ -187,6 +200,8 @@
             }
 
             mNavigationBarFrame.setBackground(null);
+
+            setIconTintInternal(calculateTargetDarkIntensity(mAppearance));
         }
 
         private void uninstallNavigationBarFrameIfNecessary() {
@@ -389,9 +404,41 @@
         }
 
         @Override
+        public void onSystemBarAppearanceChanged(@Appearance int appearance) {
+            if (mDestroyed) {
+                return;
+            }
+
+            mAppearance = appearance;
+
+            if (mNavigationBarFrame == null) {
+                return;
+            }
+
+            final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance);
+            setIconTintInternal(targetDarkIntensity);
+        }
+
+        private void setIconTintInternal(float darkIntensity) {
+            final NavigationBarView navigationBarView =
+                    mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance);
+            if (navigationBarView == null) {
+                return;
+            }
+            navigationBarView.setDarkIntensity(darkIntensity);
+        }
+
+        @FloatRange(from = 0.0f, to = 1.0f)
+        private static float calculateTargetDarkIntensity(@Appearance int appearance) {
+            final boolean lightNavBar = (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
+            return lightNavBar ? 1.0f : 0.0f;
+        }
+
+        @Override
         public String toDebugString() {
             return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
                     + " mNavigationBarFrame=" + mNavigationBarFrame
+                    + " mAppearance=0x" + Integer.toHexString(mAppearance)
                     + "}";
         }
     }
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 6c8eb41..0893d2a 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -23,7 +23,6 @@
 
 import android.annotation.IntDef;
 import android.app.Dialog;
-import android.content.Context;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
@@ -32,6 +31,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowInsetsController;
 import android.view.WindowManager;
 
 import java.lang.annotation.Retention;
@@ -47,6 +47,7 @@
 
     private final KeyEvent.DispatcherState mDispatcherState;
     private final Rect mBounds = new Rect();
+    private final InputMethodService mService;
 
     @Retention(SOURCE)
     @IntDef(value = {WindowState.TOKEN_PENDING, WindowState.TOKEN_SET,
@@ -120,7 +121,7 @@
     /**
      * Create a SoftInputWindow that uses a custom style.
      *
-     * @param context The Context in which the DockWindow should run. In
+     * @param service The {@link InputMethodService} in which the DockWindow should run. In
      *        particular, it uses the window manager and theme from this context
      *        to present its UI.
      * @param theme A style resource describing the theme to use for the window.
@@ -129,8 +130,10 @@
      *        using styles. This theme is applied on top of the current theme in
      *        <var>context</var>. If 0, the default dialog theme will be used.
      */
-    SoftInputWindow(Context context, int theme, KeyEvent.DispatcherState dispatcherState) {
-        super(context, theme);
+    SoftInputWindow(InputMethodService service, int theme,
+            KeyEvent.DispatcherState dispatcherState) {
+        super(service, theme);
+        mService = service;
         mDispatcherState = dispatcherState;
     }
 
@@ -261,6 +264,11 @@
         }
     }
 
+    @Override
+    public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
+        mService.mNavigationBarController.onSystemBarAppearanceChanged(appearance);
+    }
+
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         mBounds.dumpDebug(proto, BOUNDS);
diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
index cd85736..4adc84b 100644
--- a/core/java/android/inputmethodservice/navigationbar/DeadZone.java
+++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
@@ -27,6 +27,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.SystemClock;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -46,6 +47,20 @@
     public static final int VERTICAL = 1;  // Consume taps along the left edge.
 
     private static final boolean CHATTY = true; // print to logcat when we eat a click
+
+    private static final FloatProperty<DeadZone> FLASH_PROPERTY =
+            new FloatProperty<DeadZone>("DeadZoneFlash") {
+        @Override
+        public void setValue(DeadZone object, float value) {
+            object.setFlash(value);
+        }
+
+        @Override
+        public Float get(DeadZone object) {
+            return object.getFlash();
+        }
+    };
+
     private final NavigationBarView mNavigationBarView;
 
     private boolean mShouldFlash;
@@ -63,7 +78,7 @@
     private final Runnable mDebugFlash = new Runnable() {
         @Override
         public void run() {
-            ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+            ObjectAnimator.ofFloat(DeadZone.this, FLASH_PROPERTY, 1f, 0f).setDuration(150).start();
         }
     };
 
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 5554137..036607b 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,6 +25,12 @@
 import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
 import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
 import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
+import static android.net.eap.EapSessionConfig.EapMsChapV2Config;
+import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
+import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
+import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
+import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
+import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.Preconditions.checkStringNotEmpty;
@@ -34,6 +40,14 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.content.pm.PackageManager;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.security.Credentials;
 import android.util.Log;
 
@@ -644,6 +658,102 @@
         return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs));
     }
 
+    private static void checkBuilderSetter(boolean constructedFromIkeTunConParams,
+            @NonNull String message) {
+        if (constructedFromIkeTunConParams) {
+            throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams "
+                    + "should not set " + message);
+        }
+    }
+
+    private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) {
+        final IkeAuthConfig config = params.getLocalAuthConfig();
+        if (config instanceof IkeAuthDigitalSignLocalConfig) {
+            return TYPE_IKEV2_IPSEC_RSA;
+        } else if (config instanceof IkeAuthEapConfig) {
+            return TYPE_IKEV2_IPSEC_USER_PASS;
+        } else if (config instanceof IkeAuthPskConfig) {
+            return TYPE_IKEV2_IPSEC_PSK;
+        } else {
+            throw new IllegalStateException("Invalid local IkeAuthConfig");
+        }
+    }
+
+    @Nullable
+    private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) {
+        if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
+
+        final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
+        final EapMsChapV2Config eapMsChapV2Config =
+                ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
+        return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null;
+    }
+
+    @Nullable
+    private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) {
+        if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
+
+        final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
+        final EapMsChapV2Config eapMsChapV2Config =
+                ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
+        return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null;
+    }
+
+    @Nullable
+    private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) {
+        if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
+
+        final IkeAuthDigitalSignLocalConfig config =
+                (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
+        return config.getClientEndCertificate();
+    }
+
+    @Nullable
+    private static X509Certificate getServerRootCaCertFromIkeSession(
+            @NonNull IkeSessionParams params) {
+        if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null;
+
+        final IkeAuthDigitalSignRemoteConfig config =
+                (IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig();
+        return config.getRemoteCaCert();
+    }
+
+    @Nullable
+    private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) {
+        if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
+
+        final IkeAuthDigitalSignLocalConfig config =
+                (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
+        return config.getPrivateKey();
+    }
+
+    @Nullable
+    private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) {
+        if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null;
+
+        final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig();
+        return config.getPsk();
+    }
+
+    @NonNull
+    private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) {
+        final IkeIdentification ident = params.getLocalIdentification();
+        // Refer to VpnIkev2Utils.parseIkeIdentification().
+        if (ident instanceof IkeKeyIdIdentification) {
+            return "@#" + new String(((IkeKeyIdIdentification) ident).keyId);
+        } else if (ident instanceof IkeRfc822AddrIdentification) {
+            return "@@" + ((IkeRfc822AddrIdentification) ident).rfc822Name;
+        } else if (ident instanceof IkeFqdnIdentification) {
+            return "@" + ((IkeFqdnIdentification) ident).fqdn;
+        } else if (ident instanceof IkeIpv4AddrIdentification) {
+            return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress();
+        } else if (ident instanceof IkeIpv6AddrIdentification) {
+            return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress();
+        } else {
+            throw new IllegalArgumentException("Unknown IkeIdentification to get user identity");
+        }
+    }
+
     /** A incremental builder for IKEv2 VPN profiles */
     public static final class Builder {
         private int mType = -1;
@@ -671,6 +781,7 @@
         private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
         private boolean mIsRestrictedToTestNetworks = false;
         private boolean mExcludeLocalRoutes = false;
+        @Nullable private IkeTunnelConnectionParams mIkeTunConnParams;
 
         /**
          * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
@@ -687,6 +798,32 @@
             mUserIdentity = identity;
         }
 
+        /**
+         * Creates a new builder from a {@link IkeTunnelConnectionParams}
+         *
+         * @param ikeTunConnParams the {@link IkeTunnelConnectionParams} contains IKEv2
+         *                         configurations
+         */
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+        public Builder(@NonNull IkeTunnelConnectionParams ikeTunConnParams) {
+            checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams");
+
+            mIkeTunConnParams = ikeTunConnParams;
+
+            final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
+            mServerAddr = ikeSessionParams.getServerHostname();
+
+            mType = getTypeFromIkeSession(ikeSessionParams);
+            mUserCert = getUserCertFromIkeSession(ikeSessionParams);
+            mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
+            mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams);
+            mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
+            mUsername = getUsernameFromIkeSession(ikeSessionParams);
+            mPassword = getPasswordFromIkeSession(ikeSessionParams);
+            mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams);
+            mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams);
+        }
+
         private void resetAuthParams() {
             mPresharedKey = null;
             mServerRootCaCert = null;
@@ -719,6 +856,7 @@
                 @Nullable X509Certificate serverRootCa) {
             checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
             checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
+            checkBuilderSetter(mIkeTunConnParams != null, "authUsernamePassword");
 
             // Test to make sure all auth params can be encoded safely.
             if (serverRootCa != null) checkCert(serverRootCa);
@@ -755,6 +893,7 @@
                 @Nullable X509Certificate serverRootCa) {
             checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
             checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
+            checkBuilderSetter(mIkeTunConnParams != null, "authDigitalSignature");
 
             // Test to make sure all auth params can be encoded safely.
             checkCert(userCert);
@@ -782,6 +921,7 @@
         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAuthPsk(@NonNull byte[] psk) {
             checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
+            checkBuilderSetter(mIkeTunConnParams != null, "authPsk");
 
             resetAuthParams();
             mPresharedKey = psk;
@@ -931,8 +1071,6 @@
          *
          * Note that because the local traffic will always bypass the VPN,
          * it is not possible to set this flag on a non-bypassable VPN.
-         *
-         * @hide TODO(184750836): unhide once the implementation is completed
          */
         @NonNull
         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
index 777a90c..3c45799 100644
--- a/core/java/android/net/PlatformVpnProfile.java
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -83,8 +83,6 @@
 
     /**
      * Returns if the local traffic is exempted from the VPN.
-     *
-     * @hide TODO(184750836): unhide once the implementation is completed
      */
     public final boolean getExcludeLocalRoutes() {
         return mExcludeLocalRoutes;
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
new file mode 100644
index 0000000..24c22a9
--- /dev/null
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -0,0 +1,520 @@
+/*
+ * 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.net.netstats;
+
+import static android.app.usage.NetworkStatsManager.PREFIX_UID;
+import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
+import static android.app.usage.NetworkStatsManager.PREFIX_XT;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+
+import android.annotation.NonNull;
+import android.net.NetworkIdentity;
+import android.net.NetworkStatsCollection;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Environment;
+import android.util.AtomicFile;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastDataInput;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper class to read old version of persistent network statistics, the implementation is
+ * intended to be modified by OEM partners to accommodate their custom changes.
+ * @hide
+ */
+// @SystemApi(client = MODULE_LIBRARIES)
+public class NetworkStatsDataMigrationUtils {
+
+    private static final HashMap<String, String> sPrefixLegacyFileNameMap =
+            new HashMap<String, String>() {{
+                put(PREFIX_XT, "netstats_xt.bin");
+                put(PREFIX_UID, "netstats_uid.bin");
+                put(PREFIX_UID_TAG, "netstats_uid.bin");
+            }};
+
+    // These version constants are copied from NetworkStatsCollection/History, which is okay for
+    // OEMs to modify to adapt their own logic.
+    private static class CollectionVersion {
+        static final int VERSION_NETWORK_INIT = 1;
+
+        static final int VERSION_UID_INIT = 1;
+        static final int VERSION_UID_WITH_IDENT = 2;
+        static final int VERSION_UID_WITH_TAG = 3;
+        static final int VERSION_UID_WITH_SET = 4;
+
+        static final int VERSION_UNIFIED_INIT = 16;
+    }
+
+    private static class HistoryVersion {
+        static final int VERSION_INIT = 1;
+        static final int VERSION_ADD_PACKETS = 2;
+        static final int VERSION_ADD_ACTIVE = 3;
+    }
+
+    private static class IdentitySetVersion {
+        static final int VERSION_INIT = 1;
+        static final int VERSION_ADD_ROAMING = 2;
+        static final int VERSION_ADD_NETWORK_ID = 3;
+        static final int VERSION_ADD_METERED = 4;
+        static final int VERSION_ADD_DEFAULT_NETWORK = 5;
+        static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+    }
+
+    /**
+     * File header magic number: "ANET". The definition is copied from NetworkStatsCollection,
+     * but it is fine for OEM to re-define to their own value to adapt the legacy file reading
+     * logic.
+     */
+    private static final int FILE_MAGIC = 0x414E4554;
+    /** Default buffer size from BufferedInputStream */
+    private static final int BUFFER_SIZE = 8192;
+
+    // Constructing this object is not allowed.
+    private NetworkStatsDataMigrationUtils() {
+    }
+
+    // Used to read files at /data/system/netstats_*.bin.
+    @NonNull
+    private static File getPlatformSystemDir() {
+        return new File(Environment.getDataDirectory(), "system");
+    }
+
+    // Used to read files at /data/system/netstats/<tag>.<start>-<end>.
+    @NonNull
+    private static File getPlatformBaseDir() {
+        File baseDir = new File(getPlatformSystemDir(), "netstats");
+        baseDir.mkdirs();
+        return baseDir;
+    }
+
+    // Get /data/system/netstats_*.bin legacy files. Does not check for existence.
+    @NonNull
+    private static File getLegacyBinFileForPrefix(@NonNull String prefix) {
+        return new File(getPlatformSystemDir(), sPrefixLegacyFileNameMap.get(prefix));
+    }
+
+    // List /data/system/netstats/[xt|uid|uid_tag].<start>-<end> legacy files.
+    @NonNull
+    private static ArrayList<File> getPlatformFileListForPrefix(@NonNull String prefix) {
+        final ArrayList<File> list = new ArrayList<>();
+        final File platformFiles = new File(getPlatformBaseDir(), "netstats");
+        if (platformFiles.exists()) {
+            for (String name : platformFiles.list()) {
+                // Skip when prefix doesn't match.
+                if (!name.startsWith(prefix + ".")) continue;
+
+                list.add(new File(platformFiles, name));
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Read legacy persisted network stats from disk. This function provides a default
+     * implementation to read persisted network stats from two different locations.
+     * And this is intended to be modified by OEM to read from custom file format or
+     * locations if necessary.
+     *
+     * @param prefix         Type of data which is being read by the service.
+     * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+     * @return {@link NetworkStatsCollection} instance.
+     */
+    @NonNull
+    public static NetworkStatsCollection readPlatformCollectionLocked(
+            @NonNull String prefix, long bucketDuration) throws IOException {
+        final NetworkStatsCollection.Builder builder =
+                new NetworkStatsCollection.Builder(bucketDuration);
+
+        // Import /data/system/netstats_uid.bin legacy files if exists.
+        switch (prefix) {
+            case PREFIX_UID:
+            case PREFIX_UID_TAG:
+                final File uidFile = getLegacyBinFileForPrefix(prefix);
+                if (uidFile.exists()) {
+                    readLegacyUid(builder, uidFile, PREFIX_UID_TAG.equals(prefix) ? true : false);
+                }
+                break;
+            default:
+                // Ignore other types.
+        }
+
+        // Import /data/system/netstats/[xt|uid|uid_tag].<start>-<end> legacy files if exists.
+        final ArrayList<File> platformFiles = getPlatformFileListForPrefix(prefix);
+        for (final File platformFile : platformFiles) {
+            if (platformFile.exists()) {
+                readPlatformCollection(builder, platformFile);
+            }
+        }
+
+        return builder.build();
+    }
+
+    private static void readPlatformCollection(@NonNull NetworkStatsCollection.Builder builder,
+            @NonNull File file) throws IOException {
+        final FileInputStream is = new FileInputStream(file);
+        final FastDataInput dataIn = new FastDataInput(is, BUFFER_SIZE);
+        try {
+            readPlatformCollection(builder, dataIn);
+        } finally {
+            IoUtils.closeQuietly(dataIn);
+        }
+    }
+
+    /**
+     * Helper function to read old version of NetworkStatsCollections that resided in the platform.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static void readPlatformCollection(@NonNull NetworkStatsCollection.Builder builder,
+            @NonNull DataInput in) throws IOException {
+        // verify file magic header intact
+        final int magic = in.readInt();
+        if (magic != FILE_MAGIC) {
+            throw new ProtocolException("unexpected magic: " + magic);
+        }
+
+        final int version = in.readInt();
+        switch (version) {
+            case CollectionVersion.VERSION_UNIFIED_INIT: {
+                // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+                final int identSize = in.readInt();
+                for (int i = 0; i < identSize; i++) {
+                    final Set<NetworkIdentity> ident = readPlatformNetworkIdentitySet(in);
+
+                    final int size = in.readInt();
+                    for (int j = 0; j < size; j++) {
+                        final int uid = in.readInt();
+                        final int set = in.readInt();
+                        final int tag = in.readInt();
+
+                        final NetworkStatsCollection.Key key = new NetworkStatsCollection.Key(
+                                ident, uid, set, tag);
+                        final NetworkStatsHistory history = readPlatformHistory(in);
+                        builder.addEntry(key, history);
+                    }
+                }
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+    }
+
+    // Copied from NetworkStatsHistory#DataStreamUtils.
+    private static long[] readFullLongArray(DataInput in) throws IOException {
+        final int size = in.readInt();
+        if (size < 0) throw new ProtocolException("negative array size");
+        final long[] values = new long[size];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = in.readLong();
+        }
+        return values;
+    }
+
+    // Copied from NetworkStatsHistory#DataStreamUtils.
+    private static long[] readVarLongArray(@NonNull DataInput in) throws IOException {
+        final int size = in.readInt();
+        if (size == -1) return null;
+        if (size < 0) throw new ProtocolException("negative array size");
+        final long[] values = new long[size];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = readVarLong(in);
+        }
+        return values;
+    }
+
+    /**
+     * Read variable-length {@link Long} using protobuf-style approach.
+     */
+    // Copied from NetworkStatsHistory#DataStreamUtils.
+    private static long readVarLong(DataInput in) throws IOException {
+        int shift = 0;
+        long result = 0;
+        while (shift < 64) {
+            byte b = in.readByte();
+            result |= (long) (b & 0x7F) << shift;
+            if ((b & 0x80) == 0) {
+                return result;
+            }
+            shift += 7;
+        }
+        throw new ProtocolException("malformed var long");
+    }
+
+    // Copied from NetworkIdentitySet.
+    private static String readOptionalString(DataInput in) throws IOException {
+        if (in.readByte() != 0) {
+            return in.readUTF();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * This is copied from NetworkStatsHistory#NetworkStatsHistory(DataInput in). But it is fine
+     * for OEM to re-write the logic to adapt the legacy file reading.
+     */
+    @NonNull
+    private static NetworkStatsHistory readPlatformHistory(@NonNull DataInput in)
+            throws IOException {
+        final long bucketDuration;
+        final long[] bucketStart;
+        final long[] rxBytes;
+        final long[] rxPackets;
+        final long[] txBytes;
+        final long[] txPackets;
+        final long[] operations;
+        final int bucketCount;
+        long[] activeTime = new long[0];
+
+        final int version = in.readInt();
+        switch (version) {
+            case HistoryVersion.VERSION_INIT: {
+                bucketDuration = in.readLong();
+                bucketStart = readFullLongArray(in);
+                rxBytes = readFullLongArray(in);
+                rxPackets = new long[bucketStart.length];
+                txBytes = readFullLongArray(in);
+                txPackets = new long[bucketStart.length];
+                operations = new long[bucketStart.length];
+                bucketCount = bucketStart.length;
+                break;
+            }
+            case HistoryVersion.VERSION_ADD_PACKETS:
+            case HistoryVersion.VERSION_ADD_ACTIVE: {
+                bucketDuration = in.readLong();
+                bucketStart = readVarLongArray(in);
+                activeTime = (version >= HistoryVersion.VERSION_ADD_ACTIVE)
+                        ? readVarLongArray(in)
+                        : new long[bucketStart.length];
+                rxBytes = readVarLongArray(in);
+                rxPackets = readVarLongArray(in);
+                txBytes = readVarLongArray(in);
+                txPackets = readVarLongArray(in);
+                operations = readVarLongArray(in);
+                bucketCount = bucketStart.length;
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+
+        final NetworkStatsHistory.Builder historyBuilder =
+                new NetworkStatsHistory.Builder(bucketDuration, bucketCount);
+        for (int i = 0; i < bucketCount; i++) {
+            final NetworkStatsHistory.Entry entry = new NetworkStatsHistory.Entry(
+                    bucketStart[i], activeTime[i],
+                    rxBytes[i], rxPackets[i], txBytes[i], txPackets[i], operations[i]);
+            historyBuilder.addEntry(entry);
+        }
+
+        return historyBuilder.build();
+    }
+
+    @NonNull
+    private static Set<NetworkIdentity> readPlatformNetworkIdentitySet(@NonNull DataInput in)
+            throws IOException {
+        final int version = in.readInt();
+        final int size = in.readInt();
+        final Set<NetworkIdentity> set = new HashSet<>();
+        for (int i = 0; i < size; i++) {
+            if (version <= IdentitySetVersion.VERSION_INIT) {
+                final int ignored = in.readInt();
+            }
+            final int type = in.readInt();
+            final int ratType = in.readInt();
+            final String subscriberId = readOptionalString(in);
+            final String networkId;
+            if (version >= IdentitySetVersion.VERSION_ADD_NETWORK_ID) {
+                networkId = readOptionalString(in);
+            } else {
+                networkId = null;
+            }
+            final boolean roaming;
+            if (version >= IdentitySetVersion.VERSION_ADD_ROAMING) {
+                roaming = in.readBoolean();
+            } else {
+                roaming = false;
+            }
+
+            final boolean metered;
+            if (version >= IdentitySetVersion.VERSION_ADD_METERED) {
+                metered = in.readBoolean();
+            } else {
+                // If this is the old data and the type is mobile, treat it as metered. (Note that
+                // if this is a mobile network, TYPE_MOBILE is the only possible type that could be
+                // used.)
+                metered = (type == TYPE_MOBILE);
+            }
+
+            final boolean defaultNetwork;
+            if (version >= IdentitySetVersion.VERSION_ADD_DEFAULT_NETWORK) {
+                defaultNetwork = in.readBoolean();
+            } else {
+                defaultNetwork = true;
+            }
+
+            final int oemNetCapabilities;
+            if (version >= IdentitySetVersion.VERSION_ADD_OEM_MANAGED_NETWORK) {
+                oemNetCapabilities = in.readInt();
+            } else {
+                oemNetCapabilities = NetworkIdentity.OEM_NONE;
+            }
+
+            // Legacy files might contain TYPE_MOBILE_* types which were deprecated in later
+            // releases. For backward compatibility, record them as TYPE_MOBILE instead.
+            final int collapsedLegacyType = getCollapsedLegacyType(type);
+            final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
+                    .setType(collapsedLegacyType)
+                    .setSubscriberId(subscriberId)
+                    .setWifiNetworkKey(networkId)
+                    .setRoaming(roaming).setMetered(metered)
+                    .setDefaultNetwork(defaultNetwork)
+                    .setOemManaged(oemNetCapabilities);
+            if (type == TYPE_MOBILE && ratType != NetworkTemplate.NETWORK_TYPE_ALL) {
+                builder.setRatType(ratType);
+            }
+            set.add(builder.build());
+        }
+        return set;
+    }
+
+    private static int getCollapsedLegacyType(int networkType) {
+        // The constants are referenced from ConnectivityManager#TYPE_MOBILE_*.
+        switch (networkType) {
+            case TYPE_MOBILE:
+            case TYPE_MOBILE_SUPL:
+            case TYPE_MOBILE_MMS:
+            case TYPE_MOBILE_DUN:
+            case TYPE_MOBILE_HIPRI:
+            case 10 /* TYPE_MOBILE_FOTA */:
+            case 11 /* TYPE_MOBILE_IMS */:
+            case 12 /* TYPE_MOBILE_CBS */:
+            case 14 /* TYPE_MOBILE_IA */:
+            case 15 /* TYPE_MOBILE_EMERGENCY */:
+                return TYPE_MOBILE;
+        }
+        return networkType;
+    }
+
+    private static void readLegacyUid(@NonNull NetworkStatsCollection.Builder builder,
+            @NonNull File uidFile, boolean onlyTaggedData) throws IOException {
+        final AtomicFile inputFile = new AtomicFile(uidFile);
+        DataInputStream in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
+        try {
+            readLegacyUid(builder, in, onlyTaggedData);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    /**
+     * Read legacy Uid statistics file format into the collection.
+     *
+     * This is copied from {@code NetworkStatsCollection#readLegacyUid}.
+     * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
+     *
+     * @param taggedData whether to read tagged data. For legacy uid files, the tagged
+     *                   data was stored in the same binary file with non-tagged data.
+     *                   But in later releases, these data should be kept in different
+     *                   recorders.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void readLegacyUid(@NonNull NetworkStatsCollection.Builder builder,
+            @NonNull DataInput in, boolean taggedData) throws IOException {
+        try {
+            // verify file magic header intact
+            final int magic = in.readInt();
+            if (magic != FILE_MAGIC) {
+                throw new ProtocolException("unexpected magic: " + magic);
+            }
+
+            final int version = in.readInt();
+            switch (version) {
+                case CollectionVersion.VERSION_UID_INIT: {
+                    // uid := size *(UID NetworkStatsHistory)
+                    // drop this data version, since we don't have a good
+                    // mapping into NetworkIdentitySet.
+                    break;
+                }
+                case CollectionVersion.VERSION_UID_WITH_IDENT: {
+                    // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
+                    // drop this data version, since this version only existed
+                    // for a short time.
+                    break;
+                }
+                case CollectionVersion.VERSION_UID_WITH_TAG:
+                case CollectionVersion.VERSION_UID_WITH_SET: {
+                    // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+                    final int identSize = in.readInt();
+                    for (int i = 0; i < identSize; i++) {
+                        final Set<NetworkIdentity> ident = readPlatformNetworkIdentitySet(in);
+
+                        final int size = in.readInt();
+                        for (int j = 0; j < size; j++) {
+                            final int uid = in.readInt();
+                            final int set = (version >= CollectionVersion.VERSION_UID_WITH_SET)
+                                    ? in.readInt()
+                                    : SET_DEFAULT;
+                            final int tag = in.readInt();
+
+                            final NetworkStatsCollection.Key key = new NetworkStatsCollection.Key(
+                                    ident, uid, set, tag);
+                            final NetworkStatsHistory history = readPlatformHistory(in);
+
+                            if ((tag == TAG_NONE) != taggedData) {
+                                builder.addEntry(key, history);
+                            }
+                        }
+                    }
+                    break;
+                }
+                default: {
+                    throw new ProtocolException("unknown version: " + version);
+                }
+            }
+        } catch (FileNotFoundException | ProtocolException e) {
+            // missing stats is okay, probably first boot
+        }
+    }
+}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 47a272c..de76c8f 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -673,6 +673,7 @@
         public final int firstCustomConsumedPowerColumn;
         public final int firstCustomUsageDurationColumn;
         public final int columnCount;
+        public final Key[][] processStateKeys;
 
         private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
                 boolean powerModelsIncluded, boolean includeProcessStateData) {
@@ -728,6 +729,28 @@
                 keys[componentId] = perComponentKeys.toArray(KEY_ARRAY);
             }
 
+            if (includeProcessStateData) {
+                processStateKeys = new Key[BatteryConsumer.PROCESS_STATE_COUNT][];
+                ArrayList<Key> perProcStateKeys = new ArrayList<>();
+                for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) {
+                    if (processState == PROCESS_STATE_UNSPECIFIED) {
+                        continue;
+                    }
+
+                    perProcStateKeys.clear();
+                    for (int i = 0; i < keys.length; i++) {
+                        for (int j = 0; j < keys[i].length; j++) {
+                            if (keys[i][j].processState == processState) {
+                                perProcStateKeys.add(keys[i][j]);
+                            }
+                        }
+                    }
+                    processStateKeys[processState] = perProcStateKeys.toArray(KEY_ARRAY);
+                }
+            } else {
+                processStateKeys = null;
+            }
+
             firstCustomConsumedPowerColumn = columnIndex;
             columnIndex += customPowerComponentCount;
 
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 425e797..df5b7bc 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -65,6 +65,11 @@
     boolean isBatteryDischargePredictionPersonalized();
     boolean isDeviceIdleMode();
     boolean isLightDeviceIdleMode();
+    boolean isLowPowerStandbySupported();
+    boolean isLowPowerStandbyEnabled();
+    void setLowPowerStandbyEnabled(boolean enabled);
+    void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance);
+    void forceLowPowerStandbyActive(boolean active);
 
     @UnsupportedAppUsage
     void reboot(boolean confirm, String reason, boolean wait);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index fe86874..d9c9a2b 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -62,6 +62,8 @@
     int[] getProfileIds(int userId, boolean enabledOnly);
     boolean isUserTypeEnabled(in String userType);
     boolean canAddMoreUsersOfType(in String userType);
+    int getRemainingCreatableUserCount(in String userType);
+    int getRemainingCreatableProfileCount(in String userType, int userId, boolean allowedToRemoveOne);
     boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne);
     boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
     UserInfo getProfileParent(int userId);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e8b3ae9..3bc3ec8 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3711,10 +3711,10 @@
         final int m = list.size();
         int i = 0;
         for (; i < m && i < n; i++) {
-            list.set(i, readParcelableInternal(cl, clazz));
+            list.set(i, (T) readParcelableInternal(cl, clazz));
         }
         for (; i < n; i++) {
-            list.add(readParcelableInternal(cl, clazz));
+            list.add((T) readParcelableInternal(cl, clazz));
         }
         for (; i < m; i++) {
             list.remove(n);
@@ -4217,8 +4217,7 @@
      * trying to instantiate an element.
      */
     @Nullable
-    public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
-            @NonNull Class<? super T> clazz) {
+    public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
         Objects.requireNonNull(clazz);
         return readParcelableInternal(loader, clazz);
     }
@@ -4228,8 +4227,7 @@
      */
     @SuppressWarnings("unchecked")
     @Nullable
-    private <T extends Parcelable> T readParcelableInternal(@Nullable ClassLoader loader,
-            @Nullable Class<? super T> clazz) {
+    private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
         Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
         if (creator == null) {
             return null;
@@ -4465,8 +4463,7 @@
      * deserializing the object.
      */
     @Nullable
-    public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
-            @NonNull Class<? super T> clazz) {
+    public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
         Objects.requireNonNull(clazz);
         return readSerializableInternal(
                 loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4476,8 +4473,8 @@
      * @param clazz The type of the serializable expected or {@code null} for performing no checks
      */
     @Nullable
-    private <T extends Serializable> T readSerializableInternal(@Nullable final ClassLoader loader,
-            @Nullable Class<? super T> clazz) {
+    private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
+            @Nullable Class<T> clazz) {
         String name = readString();
         if (name == null) {
             // For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 590494c..48e1116 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -16,7 +16,6 @@
 package android.os;
 
 import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
-import static android.os.BatteryConsumer.POWER_COMPONENT_COUNT;
 import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
 import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
 import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
@@ -60,19 +59,16 @@
             return mData.getDouble(mData.getKeyOrThrow(dimensions.powerComponent,
                     dimensions.processState).mPowerColumnIndex);
         } else if (dimensions.processState != PROCESS_STATE_ANY) {
-            boolean foundSome = false;
-            double totalPowerMah = 0;
-            for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
-                BatteryConsumer.Key key = mData.getKey(componentId, dimensions.processState);
-                if (key != null) {
-                    foundSome = true;
-                    totalPowerMah += mData.getDouble(key.mPowerColumnIndex);
-                }
-            }
-            if (!foundSome) {
+            if (!mData.layout.processStateDataIncluded) {
                 throw new IllegalArgumentException(
                         "No data included in BatteryUsageStats for " + dimensions);
             }
+            final BatteryConsumer.Key[] keys =
+                    mData.layout.processStateKeys[dimensions.processState];
+            double totalPowerMah = 0;
+            for (int i = keys.length - 1; i >= 0; i--) {
+                totalPowerMah += mData.getDouble(keys[i].mPowerColumnIndex);
+            }
             return totalPowerMah;
         } else {
             return mData.getDouble(mData.layout.totalConsumedPowerColumnIndex);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 881fced..5bd8588 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -216,6 +216,17 @@
     public static final int UNIMPORTANT_FOR_LOGGING = 0x40000000;
 
     /**
+     * Wake lock flag: This wake lock should be held by the system.
+     *
+     * <p>Meant to allow tests to keep the device awake even when power restrictions are active.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public static final int SYSTEM_WAKELOCK = 0x80000000;
+
+    /**
      * Flag for {@link WakeLock#release WakeLock.release(int)}: Defer releasing a
      * {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK} wake lock until the proximity sensor
      * indicates that an object is not in close proximity.
@@ -2146,6 +2157,105 @@
     }
 
     /**
+     * Returns true if Low Power Standby is supported on this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public boolean isLowPowerStandbySupported() {
+        try {
+            return mService.isLowPowerStandbySupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns true if Low Power Standby is enabled.
+     *
+     * <p>When Low Power Standby is enabled, apps (including apps running foreground services) are
+     * subject to additional restrictions while the device is non-interactive, outside of device
+     * idle maintenance windows: Their network access is disabled, and any wakelocks they hold are
+     * ignored.
+     *
+     * <p>When Low Power Standby is enabled or disabled, a Intent with action
+     * {@link #ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED} is broadcast to registered receivers.
+     */
+    public boolean isLowPowerStandbyEnabled() {
+        try {
+            return mService.isLowPowerStandbyEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether Low Power Standby is enabled.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @see #isLowPowerStandbyEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setLowPowerStandbyEnabled(boolean enabled) {
+        try {
+            mService.setLowPowerStandbyEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set whether Low Power Standby should be active during doze maintenance mode.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @see #isLowPowerStandbyEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+        try {
+            mService.setLowPowerStandbyActiveDuringMaintenance(activeDuringMaintenance);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force Low Power Standby restrictions to be active.
+     * Does nothing if Low Power Standby is not supported.
+     *
+     * @see #isLowPowerStandbySupported()
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+            android.Manifest.permission.DEVICE_POWER
+    })
+    public void forceLowPowerStandbyActive(boolean active) {
+        try {
+            mService.forceLowPowerStandbyActive(active);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return whether the given application package name is on the device's power allowlist.
      * Apps can be placed on the allowlist through the settings UI invoked by
      * {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}.
@@ -2631,6 +2741,16 @@
             = "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED";
 
     /**
+     * Intent that is broadcast when Low Power Standby is enabled or disabled.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @see #isLowPowerStandbyEnabled()
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED =
+            "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
+
+    /**
      * Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
      * @hide
      */
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index eb18b96..ec4d3b6 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -184,6 +184,21 @@
 
     public abstract void setDeviceIdleTempWhitelist(int[] appids);
 
+    /**
+     * Updates the Low Power Standby allowlist.
+     *
+     * @param uids UIDs that are exempt from Low Power Standby restrictions
+     */
+    public abstract void setLowPowerStandbyAllowlist(int[] uids);
+
+    /**
+     * Used by LowPowerStandbyController to notify the power manager that Low Power Standby's
+     * active state has changed.
+     *
+     * @param active {@code true} to activate Low Power Standby, {@code false} to turn it off.
+     */
+    public abstract void setLowPowerStandbyActive(boolean active);
+
     public abstract void startUidChanges();
 
     public abstract void finishUidChanges();
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index bd65a41..7b8d34b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4029,6 +4029,58 @@
     }
 
     /**
+     * Returns the remaining number of users of the given type that can be created.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+     * @return how many additional users can be created.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    public int getRemainingCreatableUserCount(@NonNull String userType) {
+        Objects.requireNonNull(userType, "userType must not be null");
+        try {
+            return mService.getRemainingCreatableUserCount(userType);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the remaining number of profiles that can be added to the context user.
+     * <p>Note that is applicable to any profile type (currently not including Restricted profiles).
+     *
+     * @param userType the type of profile, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param allowedToRemoveOne whether removing an existing profile of given type -if there is-
+     *                           from the context user to make up space should be taken into account
+     *                           for the calculation.
+     * @return how many additional profiles can be created.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
+            android.Manifest.permission.QUERY_USERS
+    })
+    @UserHandleAware
+    public int getRemainingCreatableProfileCount(@NonNull String userType,
+            boolean allowedToRemoveOne) {
+        Objects.requireNonNull(userType, "userType must not be null");
+        try {
+            // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
+            return mService.getRemainingCreatableProfileCount(userType, mUserId,
+                    allowedToRemoveOne);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks whether it's possible to add more managed profiles.
      * if allowedToRemoveOne is true and if the user already has a managed profile, then return if
      * we could add a new managed profile to this user after removing the existing one.
@@ -4038,6 +4090,7 @@
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.QUERY_USERS
     })
     public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
@@ -4057,6 +4110,7 @@
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.QUERY_USERS
     })
     public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
@@ -4904,8 +4958,8 @@
     public static int getMaxSupportedUsers() {
         // Don't allow multiple users on certain builds
         if (android.os.Build.ID.startsWith("JVP")) return 1;
-        return SystemProperties.getInt("fw.max_users",
-                Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
+        return Math.max(1, SystemProperties.getInt("fw.max_users",
+                Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)));
     }
 
     /**
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 8bc219b..49c0520 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -37,6 +37,7 @@
             USAGE_CLASS_UNKNOWN,
             USAGE_CLASS_ALARM,
             USAGE_CLASS_FEEDBACK,
+            USAGE_CLASS_MEDIA,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UsageClass {}
@@ -459,4 +460,3 @@
         }
     }
 }
-
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8df659d..63616da 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -291,6 +291,8 @@
     public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
     /** {@hide} */
     public static final int FLAG_INCLUDE_RECENT = 1 << 11;
+    /** {@hide} */
+    public static final int FLAG_INCLUDE_SHARED_PROFILE = 1 << 12;
 
     /** {@hide} */
     public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
@@ -1328,6 +1330,23 @@
     }
 
     /**
+     * Return the list of shared/external storage volumes currently available to
+     * the calling user and the user it shares media with
+     * CDD link : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+     * <p>
+     * This is similar to {@link StorageManager#getStorageVolumes()} except that the result also
+     * includes the volumes belonging to any user it shares media with
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+    public @NonNull List<StorageVolume> getStorageVolumesIncludingSharedProfiles() {
+        final ArrayList<StorageVolume> res = new ArrayList<>();
+        Collections.addAll(res,
+                getVolumeList(mContext.getUserId(),
+                        FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE));
+        return res;
+    }
+
+    /**
      * Return the list of shared/external storage volumes both currently and
      * recently available to the calling user.
      * <p>
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 8ee52c2..e1f112a 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,8 +16,6 @@
 
 package android.os.storage;
 
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -308,11 +306,9 @@
 
     /**
      * Returns the user that owns this volume
-     *
-     * {@hide}
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    @SystemApi(client = MODULE_LIBRARIES)
+    // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly
+    @SuppressLint("NewApi")
     public @NonNull UserHandle getOwner() {
         return mOwner;
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a563ac..14055ac 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2386,6 +2386,15 @@
             "android.settings.ENABLE_MMS_DATA_REQUEST";
 
     /**
+     * Shows restrict settings dialog when settings is blocked.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SHOW_RESTRICTED_SETTING_DIALOG =
+            "android.settings.SHOW_RESTRICTED_SETTING_DIALOG";
+
+    /**
      * Integer value that specifies the reason triggering enable MMS data notification.
      * This must be passed as an extra field to the {@link #ACTION_ENABLE_MMS_DATA_REQUEST}.
      * Extra with value of EnableMmsDataReason interface.
@@ -6574,6 +6583,8 @@
          * @hide
          */
         @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLUETOOTH_NAME = "bluetooth_name";
 
         /**
@@ -6581,6 +6592,8 @@
          * @hide
          */
         @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
 
         /**
@@ -6588,6 +6601,8 @@
          * @hide
          */
         @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
 
         /**
@@ -10259,6 +10274,13 @@
         public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
 
         /**
+         * Nearby Sharing Slice URI for the SliceProvider to
+         * read Nearby Sharing scan results and then draw the UI.
+         * @hide
+         */
+        public static final String NEARBY_SHARING_SLICE_URI = "nearby_sharing_slice_uri";
+
+        /**
          * Controls whether aware is enabled.
          * @hide
          */
@@ -10826,6 +10848,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
 
         /**
@@ -10834,6 +10858,8 @@
          * {@hide}
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
 
         /**
@@ -12371,6 +12397,8 @@
         * @hide
         */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
 
         /**
@@ -12378,6 +12406,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
 
         /**
@@ -12385,6 +12415,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
 
         /**
@@ -12392,6 +12424,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
                 "ble_scan_low_latency_window_ms";
 
@@ -12400,6 +12434,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
                 "ble_scan_low_power_interval_ms";
 
@@ -12408,6 +12444,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
                 "ble_scan_balanced_interval_ms";
 
@@ -12416,6 +12454,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
@@ -12424,6 +12464,8 @@
          * @hide
          */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
 
         /**
@@ -13156,6 +13198,8 @@
 
         /** {@hide} */
         @Readable
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("NoSettingsProvider")
         public static final String
                 BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
         /** {@hide} */
@@ -16706,6 +16750,30 @@
         public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
 
         /**
+         * Setting indicating whether Low Power Standby is enabled, if supported.
+         *
+         * Values are:
+         * 0: disabled
+         * 1: enabled
+         *
+         * @hide
+         */
+        public static final String LOW_POWER_STANDBY_ENABLED = "low_power_standby_enabled";
+
+        /**
+         * Setting indicating whether Low Power Standby is allowed to be active during doze
+         * maintenance mode.
+         *
+         * Values are:
+         * 0: Low Power Standby will be disabled during doze maintenance mode
+         * 1: Low Power Standby can be active during doze maintenance mode
+         *
+         * @hide
+         */
+        public static final String LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE =
+                "low_power_standby_active_during_maintenance";
+
+        /**
          * Settings migrated from Wear OS settings provider.
          * @hide
          */
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 86341a9..cfb6909 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -65,6 +65,16 @@
  * can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain
  * an {@link InlinePresentation} representing how the inline suggestion UI will be rendered.
  *
+ * <a name="FillDialogUI"></a>
+ * <h3>Fill Dialog UI</h3>
+ *
+ * <p>The fill dialog UI is a more conspicuous and efficient interface than dropdown UI. If autofill
+ * suggestions are available when the user clicks on a field that supports filling the dialog UI,
+ * Autofill will pop up a fill dialog. The dialog will take up a larger area to display the
+ * datasets, so it is easy for users to pay attention to the datasets and selecting a dataset.
+ * If the user focuses on the view before suggestions are available, will fall back to dropdown UI
+ * or inline suggestions.
+ *
  * <a name="Authentication"></a>
  * <h3>Dataset authentication</h3>
  *
@@ -92,10 +102,9 @@
  * <ol>
  *   <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not
  * {@link AutofillValue#isText() text} or is empty, all datasets are shown.
- *   <li>Datasets that have a filter regex (set through
- * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern)} or
- * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}) and whose
- * regex matches the view's text value converted to lower case are shown.
+ *   <li>Datasets that have a filter regex (set through {@link Field.Builder#setFilter(Pattern)}
+ *   and {@link Dataset.Builder#setField(AutofillId, Field)}) and whose regex matches the view's
+ *   text value converted to lower case are shown.
  *   <li>Datasets that do not require authentication, have a field value that is
  * {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts
  * with the lower case value of the view's text are shown.
@@ -107,11 +116,13 @@
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
     private final ArrayList<RemoteViews> mFieldPresentations;
+    private final ArrayList<RemoteViews> mFieldDialogPresentations;
     private final ArrayList<InlinePresentation> mFieldInlinePresentations;
     private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
     private final ArrayList<DatasetFieldFilter> mFieldFilters;
     @Nullable private final ClipData mFieldContent;
     private final RemoteViews mPresentation;
+    private final RemoteViews mDialogPresentation;
     @Nullable private final InlinePresentation mInlinePresentation;
     @Nullable private final InlinePresentation mInlineTooltipPresentation;
     private final IntentSender mAuthentication;
@@ -121,11 +132,13 @@
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
         mFieldPresentations = builder.mFieldPresentations;
+        mFieldDialogPresentations = builder.mFieldDialogPresentations;
         mFieldInlinePresentations = builder.mFieldInlinePresentations;
         mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations;
         mFieldFilters = builder.mFieldFilters;
         mFieldContent = builder.mFieldContent;
         mPresentation = builder.mPresentation;
+        mDialogPresentation = builder.mDialogPresentation;
         mInlinePresentation = builder.mInlinePresentation;
         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
         mAuthentication = builder.mAuthentication;
@@ -153,6 +166,12 @@
     }
 
     /** @hide */
+    public RemoteViews getFieldDialogPresentation(int index) {
+        final RemoteViews customPresentation = mFieldDialogPresentations.get(index);
+        return customPresentation != null ? customPresentation : mDialogPresentation;
+    }
+
+    /** @hide */
     public @Nullable InlinePresentation getFieldInlinePresentation(int index) {
         final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
         return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
@@ -219,6 +238,9 @@
         if (mFieldPresentations != null) {
             builder.append(", fieldPresentations=").append(mFieldPresentations.size());
         }
+        if (mFieldDialogPresentations != null) {
+            builder.append(", fieldDialogPresentations=").append(mFieldDialogPresentations.size());
+        }
         if (mFieldInlinePresentations != null) {
             builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
         }
@@ -232,6 +254,9 @@
         if (mPresentation != null) {
             builder.append(", hasPresentation");
         }
+        if (mDialogPresentation != null) {
+            builder.append(", hasDialogPresentation");
+        }
         if (mInlinePresentation != null) {
             builder.append(", hasInlinePresentation");
         }
@@ -264,11 +289,13 @@
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
         private ArrayList<RemoteViews> mFieldPresentations;
+        private ArrayList<RemoteViews> mFieldDialogPresentations;
         private ArrayList<InlinePresentation> mFieldInlinePresentations;
         private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
         private ArrayList<DatasetFieldFilter> mFieldFilters;
         @Nullable private ClipData mFieldContent;
         private RemoteViews mPresentation;
+        private RemoteViews mDialogPresentation;
         @Nullable private InlinePresentation mInlinePresentation;
         @Nullable private InlinePresentation mInlineTooltipPresentation;
         private IntentSender mAuthentication;
@@ -279,7 +306,9 @@
          * Creates a new builder.
          *
          * @param presentation The presentation used to visualize this dataset.
+         * @deprecated Use {@link #Builder(Presentations)} instead.
          */
+        @Deprecated
         public Builder(@NonNull RemoteViews presentation) {
             Objects.requireNonNull(presentation, "presentation must be non-null");
             mPresentation = presentation;
@@ -294,19 +323,34 @@
          *              as inline suggestions. If the dataset supports inline suggestions,
          *              this should not be null.
          * @hide
+         * @deprecated Use {@link #Builder(Presentations)} instead.
          */
         @SystemApi
+        @Deprecated
         public Builder(@NonNull InlinePresentation inlinePresentation) {
             Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
             mInlinePresentation = inlinePresentation;
         }
 
         /**
+         * Creates a new builder.
+         *
+         * @param presentations The presentations used to visualize this dataset.
+         */
+        public Builder(@NonNull Presentations presentations) {
+            Objects.requireNonNull(presentations, "presentations must be non-null");
+
+            mPresentation = presentations.getMenuPresentation();
+            mInlinePresentation = presentations.getInlinePresentation();
+            mInlineTooltipPresentation = presentations.getInlineTooltipPresentation();
+            mDialogPresentation = presentations.getDialogPresentation();
+        }
+
+        /**
          * Creates a new builder for a dataset where each field will be visualized independently.
          *
-         * <p>When using this constructor, fields must be set through
-         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
-         * {@link #setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}.
+         * <p>When using this constructor, a presentation must be provided for each field through
+         * {@link #setField(AutofillId, Field)}.
          */
         public Builder() {
         }
@@ -318,7 +362,9 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #Builder(Presentations)} instead.
          */
+        @Deprecated
         public @NonNull Builder setInlinePresentation(
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
@@ -339,7 +385,9 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #Builder(Presentations)} instead.
          */
+        @Deprecated
         public @NonNull Builder setInlinePresentation(
                 @NonNull InlinePresentation inlinePresentation,
                 @NonNull InlinePresentation inlineTooltipPresentation) {
@@ -479,7 +527,7 @@
                             "Content items cannot contain an Intent: content=" + content);
                 }
             }
-            setLifeTheUniverseAndEverything(id, null, null, null, null);
+            setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
             mFieldContent = content;
             return this;
         }
@@ -509,10 +557,12 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
             throwIfDestroyed();
-            setLifeTheUniverseAndEverything(id, value, null, null, null);
+            setLifeTheUniverseAndEverything(id, value, null, null, null, null, null);
             return this;
         }
 
@@ -537,12 +587,14 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation) {
             throwIfDestroyed();
             Objects.requireNonNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, null, null);
+            setLifeTheUniverseAndEverything(id, value, presentation, null, null, null, null);
             return this;
         }
 
@@ -572,13 +624,16 @@
          * @return this builder.
          * @throws IllegalStateException if the builder was constructed without a
          *         {@link RemoteViews presentation} or {@link #build()} was already called.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @Nullable Pattern filter) {
             throwIfDestroyed();
             Preconditions.checkState(mPresentation != null,
                     "Dataset presentation not set on constructor");
-            setLifeTheUniverseAndEverything(id, value, null, null, new DatasetFieldFilter(filter));
+            setLifeTheUniverseAndEverything(
+                    id, value, null, null, null, new DatasetFieldFilter(filter), null);
             return this;
         }
 
@@ -610,13 +665,15 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @Nullable Pattern filter, @NonNull RemoteViews presentation) {
             throwIfDestroyed();
             Objects.requireNonNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, null,
-                    new DatasetFieldFilter(filter));
+            setLifeTheUniverseAndEverything(id, value, presentation, null, null,
+                    new DatasetFieldFilter(filter), null);
             return this;
         }
 
@@ -641,13 +698,16 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
             Objects.requireNonNull(presentation, "presentation cannot be null");
             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
+            setLifeTheUniverseAndEverything(
+                    id, value, presentation, inlinePresentation, null, null, null);
             return this;
         }
 
@@ -672,7 +732,9 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
                 @NonNull InlinePresentation inlineTooltipPresentation) {
@@ -682,7 +744,7 @@
             Objects.requireNonNull(inlineTooltipPresentation,
                     "inlineTooltipPresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
-                    inlineTooltipPresentation, null);
+                    inlineTooltipPresentation, null, null);
             return this;
         }
 
@@ -718,15 +780,17 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @Nullable Pattern filter, @NonNull RemoteViews presentation,
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
             Objects.requireNonNull(presentation, "presentation cannot be null");
             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
-                    new DatasetFieldFilter(filter));
+            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
+                    new DatasetFieldFilter(filter), null);
             return this;
         }
 
@@ -756,7 +820,9 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          */
+        @Deprecated
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @Nullable Pattern filter, @NonNull RemoteViews presentation,
                 @NonNull InlinePresentation inlinePresentation,
@@ -767,7 +833,91 @@
             Objects.requireNonNull(inlineTooltipPresentation,
                     "inlineTooltipPresentation cannot be null");
             setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
-                    inlineTooltipPresentation, new DatasetFieldFilter(filter));
+                    inlineTooltipPresentation, new DatasetFieldFilter(filter), null);
+            return this;
+        }
+
+        /**
+         * Sets the value of a field.
+         *
+         * Before Android 13, this information could be provided using several overloaded
+         * setValue(...) methods. This method replaces those with a Builder pattern.
+         * For example, in the old workflow, the app sets a field would be:
+         * <pre class="prettyprint">
+         *  Dataset.Builder dataset = new Dataset.Builder();
+         *  if (filter != null) {
+         *      if (presentation != null) {
+         *          if (inlinePresentation != null) {
+         *              dataset.setValue(id, value, filter, presentation, inlinePresentation)
+         *          } else {
+         *              dataset.setValue(id, value, filter, presentation);
+         *          }
+         *      } else {
+         *          dataset.setValue(id, value, filter);
+         *      }
+         *  } else {
+         *      if (presentation != null) {
+         *          if (inlinePresentation != null) {
+         *              dataset.setValue(id, value, presentation, inlinePresentation)
+         *          } else {
+         *              dataset.setValue(id, value, presentation);
+         *          }
+         *      } else {
+         *          dataset.setValue(id, value);
+         *      }
+         *  }
+         *  </pre>
+         * <p>The new workflow would be:
+         * <pre class="prettyprint">
+         * Field.Builder fieldBuilder = new Field.Builder();
+         * if (value != null) {
+         *     fieldBuilder.setValue(value);
+         * }
+         * if (filter != null) {
+         *     fieldBuilder.setFilter(filter);
+         * }
+         * Presentations.Builder presentationsBuilder = new Presentations.Builder(id);
+         * if (presentation != null) {
+         *     presentationsBuilder.setMenuPresentation(presentation);
+         * }
+         * if (inlinePresentation != null) {
+         *     presentationsBuilder.setInlinePresentation(inlinePresentation);
+         * }
+         * if (dialogPresentation != null) {
+         *     presentationsBuilder.setDialogPresentation(dialogPresentation);
+         * }
+         * fieldBuilder.setPresentations(presentationsBuilder.build());
+         * dataset.setField(id, fieldBuilder.build());
+         * </pre>
+         *
+         * @see Field
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param field the fill information about the field.
+         *
+         * @throws IllegalStateException if {@link #build()} was already called.
+         *
+         * @return this builder.
+         */
+        public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) {
+            throwIfDestroyed();
+            if (field == null) {
+                setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
+            } else {
+                final DatasetFieldFilter filter = field.getFilter();
+                final Presentations presentations = field.getPresentations();
+                if (presentations == null) {
+                    setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
+                            filter, null);
+                } else {
+                    setLifeTheUniverseAndEverything(id, field.getValue(),
+                            presentations.getMenuPresentation(),
+                            presentations.getInlinePresentation(),
+                            presentations.getInlineTooltipPresentation(), filter,
+                            presentations.getDialogPresentation());
+                }
+            }
             return this;
         }
 
@@ -793,39 +943,34 @@
          * @throws IllegalStateException if {@link #build()} was already called.
          *
          * @return this builder.
-         *
+         * @deprecated Use {@link #setField(AutofillId, Field)} instead.
          * @hide
          */
+        @Deprecated
         @SystemApi
         public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable Pattern filter,
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
             Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
-                    new DatasetFieldFilter(filter));
+            setLifeTheUniverseAndEverything(id, value, null, inlinePresentation, null,
+                    new DatasetFieldFilter(filter), null);
             return this;
         }
 
         private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
                 @Nullable InlinePresentation inlinePresentation,
-                @Nullable DatasetFieldFilter filter) {
-            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null,
-                    filter);
-        }
-
-        private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
-                @Nullable AutofillValue value, @Nullable RemoteViews presentation,
-                @Nullable InlinePresentation inlinePresentation,
                 @Nullable InlinePresentation tooltip,
-                @Nullable DatasetFieldFilter filter) {
+                @Nullable DatasetFieldFilter filter,
+                @Nullable RemoteViews dialogPresentation) {
             Objects.requireNonNull(id, "id cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
                 if (existingIdx >= 0) {
                     mFieldValues.set(existingIdx, value);
                     mFieldPresentations.set(existingIdx, presentation);
+                    mFieldDialogPresentations.set(existingIdx, dialogPresentation);
                     mFieldInlinePresentations.set(existingIdx, inlinePresentation);
                     mFieldInlineTooltipPresentations.set(existingIdx, tooltip);
                     mFieldFilters.set(existingIdx, filter);
@@ -835,6 +980,7 @@
                 mFieldIds = new ArrayList<>();
                 mFieldValues = new ArrayList<>();
                 mFieldPresentations = new ArrayList<>();
+                mFieldDialogPresentations = new ArrayList<>();
                 mFieldInlinePresentations = new ArrayList<>();
                 mFieldInlineTooltipPresentations = new ArrayList<>();
                 mFieldFilters = new ArrayList<>();
@@ -842,6 +988,7 @@
             mFieldIds.add(id);
             mFieldValues.add(value);
             mFieldPresentations.add(presentation);
+            mFieldDialogPresentations.add(dialogPresentation);
             mFieldInlinePresentations.add(inlinePresentation);
             mFieldInlineTooltipPresentations.add(tooltip);
             mFieldFilters.add(filter);
@@ -853,10 +1000,7 @@
          * <p>You should not interact with this builder once this method is called.
          *
          * @throws IllegalStateException if no field was set (through
-         * {@link #setValue(AutofillId, AutofillValue)} or
-         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
-         * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}),
-         * or if {@link #build()} was already called.
+         * {@link #setField(AutofillId, Field)}), or if {@link #build()} was already called.
          *
          * @return The built dataset.
          */
@@ -897,11 +1041,13 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mPresentation, flags);
+        parcel.writeParcelable(mDialogPresentation, flags);
         parcel.writeParcelable(mInlinePresentation, flags);
         parcel.writeParcelable(mInlineTooltipPresentation, flags);
         parcel.writeTypedList(mFieldIds, flags);
         parcel.writeTypedList(mFieldValues, flags);
         parcel.writeTypedList(mFieldPresentations, flags);
+        parcel.writeTypedList(mFieldDialogPresentations, flags);
         parcel.writeTypedList(mFieldInlinePresentations, flags);
         parcel.writeTypedList(mFieldInlineTooltipPresentations, flags);
         parcel.writeTypedList(mFieldFilters, flags);
@@ -913,8 +1059,12 @@
     public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
         @Override
         public Dataset createFromParcel(Parcel parcel) {
-            final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
-            final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
+            final RemoteViews presentation = parcel.readParcelable(null,
+                    android.widget.RemoteViews.class);
+            final RemoteViews dialogPresentation = parcel.readParcelable(null,
+                    android.widget.RemoteViews.class);
+            final InlinePresentation inlinePresentation = parcel.readParcelable(null,
+                    android.service.autofill.InlinePresentation.class);
             final InlinePresentation inlineTooltipPresentation =
                     parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
             final ArrayList<AutofillId> ids =
@@ -923,27 +1073,41 @@
                     parcel.createTypedArrayList(AutofillValue.CREATOR);
             final ArrayList<RemoteViews> presentations =
                     parcel.createTypedArrayList(RemoteViews.CREATOR);
+            final ArrayList<RemoteViews> dialogPresentations =
+                    parcel.createTypedArrayList(RemoteViews.CREATOR);
             final ArrayList<InlinePresentation> inlinePresentations =
                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
             final ArrayList<InlinePresentation> inlineTooltipPresentations =
                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
             final ArrayList<DatasetFieldFilter> filters =
                     parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
-            final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class);
-            final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
+            final ClipData fieldContent = parcel.readParcelable(null,
+                    android.content.ClipData.class);
+            final IntentSender authentication = parcel.readParcelable(null,
+                    android.content.IntentSender.class);
             final String datasetId = parcel.readString();
 
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = (presentation != null) ? new Builder(presentation)
-                    : new Builder();
-            if (inlinePresentation != null) {
-                if (inlineTooltipPresentation != null) {
-                    builder.setInlinePresentation(inlinePresentation, inlineTooltipPresentation);
-                } else {
-                    builder.setInlinePresentation(inlinePresentation);
+            final Builder builder;
+            if (presentation != null || inlinePresentation != null || dialogPresentation != null) {
+                final Presentations.Builder presentationsBuilder = new Presentations.Builder();
+                if (presentation != null) {
+                    presentationsBuilder.setMenuPresentation(presentation);
                 }
+                if (inlinePresentation != null) {
+                    presentationsBuilder.setInlinePresentation(inlinePresentation);
+                }
+                if (inlineTooltipPresentation != null) {
+                    presentationsBuilder.setInlineTooltipPresentation(inlineTooltipPresentation);
+                }
+                if (dialogPresentation != null) {
+                    presentationsBuilder.setDialogPresentation(dialogPresentation);
+                }
+                builder = new Builder(presentationsBuilder.build());
+            } else {
+                builder = new Builder();
             }
 
             if (fieldContent != null) {
@@ -954,13 +1118,15 @@
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
                 final RemoteViews fieldPresentation = presentations.get(i);
+                final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
                 final InlinePresentation fieldInlinePresentation =
                         i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
                 final InlinePresentation fieldInlineTooltipPresentation =
                         i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
                 final DatasetFieldFilter filter = filters.get(i);
                 builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
-                        fieldInlinePresentation, fieldInlineTooltipPresentation, filter);
+                        fieldInlinePresentation, fieldInlineTooltipPresentation, filter,
+                        fieldDialogPresentation);
             }
             builder.setAuthentication(authentication);
             builder.setId(datasetId);
@@ -986,7 +1152,7 @@
         @Nullable
         public final Pattern pattern;
 
-        private DatasetFieldFilter(@Nullable Pattern pattern) {
+        DatasetFieldFilter(@Nullable Pattern pattern) {
             this.pattern = pattern;
         }
 
diff --git a/core/java/android/service/autofill/Field.java b/core/java/android/service/autofill/Field.java
new file mode 100644
index 0000000..b7c0d82
--- /dev/null
+++ b/core/java/android/service/autofill/Field.java
@@ -0,0 +1,165 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.DataClass;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to set all information of a field. Such as the
+ * {@link AutofillId} of the field, the {@link AutofillValue} to be autofilled,
+ * a <a href="#Filtering">explicit filter</a>, and presentations to be visualized,
+ * etc.
+ */
+public final class Field {
+
+    /**
+     * The value to be autofilled. Pass {@code null} if you do not have the value
+     * but the target view is a logical part of the dataset. For example, if the
+     * dataset needs authentication and you have no access to the value.
+     */
+    private @Nullable AutofillValue mValue;
+
+    /**
+     * Regex used to determine if the dataset should be shown in the autofill UI;
+     * when {@code null}, it disables filtering on that dataset (this is the recommended
+     * approach when {@code value} is not {@code null} and field contains sensitive data
+     * such as passwords).
+     *
+     * @see Dataset.DatasetFieldFilter
+     * @hide
+     */
+    private @Nullable Dataset.DatasetFieldFilter mFilter;
+
+    /**
+     * The presentations used to visualize this field in Autofill UI.
+     */
+    private @Nullable Presentations mPresentations;
+
+
+    /* package-private */ Field(
+            @Nullable AutofillValue value,
+            @Nullable Dataset.DatasetFieldFilter filter,
+            @Nullable Presentations presentations) {
+        this.mValue = value;
+        this.mFilter = filter;
+        this.mPresentations = presentations;
+    }
+
+    /**
+     * The value to be autofilled. Pass {@code null} if you do not have the value
+     * but the target view is a logical part of the dataset. For example, if the
+     * dataset needs authentication and you have no access to the value.
+     */
+    @DataClass.Generated.Member
+    public @Nullable AutofillValue getValue() {
+        return mValue;
+    }
+
+    /**
+     * Regex used to determine if the dataset should be shown in the autofill UI;
+     * when {@code null}, it disables filtering on that dataset (this is the recommended
+     * approach when {@code value} is not {@code null} and field contains sensitive data
+     * such as passwords).
+     *
+     * @see Dataset.DatasetFieldFilter
+     * @hide
+     */
+    public @Nullable Dataset.DatasetFieldFilter getFilter() {
+        return mFilter;
+    }
+
+    /**
+     * The presentations used to visualize this field in Autofill UI.
+     */
+    public @Nullable Presentations getPresentations() {
+        return mPresentations;
+    }
+
+    /**
+     * A builder for {@link Field}
+     */
+    public static final class Builder {
+
+        private @Nullable AutofillValue mValue = null;
+        private @Nullable Dataset.DatasetFieldFilter mFilter = null;
+        private @Nullable Presentations mPresentations = null;
+        private boolean mDestroyed = false;
+
+        public Builder() {
+        }
+
+        /**
+         * The value to be autofilled. Pass {@code null} if you do not have the value
+         * but the target view is a logical part of the dataset. For example, if the
+         * dataset needs authentication and you have no access to the value.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillValue value) {
+            checkNotUsed();
+            mValue = value;
+            return this;
+        }
+
+        /**
+         * Regex used to determine if the dataset should be shown in the autofill UI;
+         * when {@code null}, it disables filtering on that dataset (this is the recommended
+         * approach when {@code value} is not {@code null} and field contains sensitive data
+         * such as passwords).
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setFilter(@Nullable Pattern value) {
+            checkNotUsed();
+            mFilter = new Dataset.DatasetFieldFilter(value);
+            return this;
+        }
+
+        /**
+         * The presentations used to visualize this field in Autofill UI.
+         */
+        public @NonNull Builder setPresentations(@NonNull Presentations value) {
+            checkNotUsed();
+            mPresentations = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull Field build() {
+            checkNotUsed();
+            mDestroyed = true; // Mark builder used
+
+            Field o = new Field(
+                    mValue,
+                    mFilter,
+                    mPresentations);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if (mDestroyed) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 43bd410..f820f03 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -98,6 +98,12 @@
 
     // The flag value 0x20 has been defined in AutofillManager.
 
+    /**
+     * Indicates the request is coming from the activity just started.
+     * @hide
+     */
+    public static final @RequestFlags int FLAG_ACTIVITY_START = 0x40;
+
     /** @hide */
     public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
 
@@ -160,13 +166,13 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillRequest.java
+    // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/service/autofill/FillRequest.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -178,7 +184,8 @@
         FLAG_MANUAL_REQUEST,
         FLAG_COMPATIBILITY_MODE_REQUEST,
         FLAG_PASSWORD_INPUT_TYPE,
-        FLAG_VIEW_NOT_FOCUSED
+        FLAG_VIEW_NOT_FOCUSED,
+        FLAG_ACTIVITY_START
     })
     @Retention(RetentionPolicy.SOURCE)
     @DataClass.Generated.Member
@@ -202,6 +209,8 @@
                     return "FLAG_PASSWORD_INPUT_TYPE";
             case FLAG_VIEW_NOT_FOCUSED:
                     return "FLAG_VIEW_NOT_FOCUSED";
+            case FLAG_ACTIVITY_START:
+                    return "FLAG_ACTIVITY_START";
             default: return Integer.toHexString(value);
         }
     }
@@ -264,7 +273,8 @@
                 FLAG_MANUAL_REQUEST
                         | FLAG_COMPATIBILITY_MODE_REQUEST
                         | FLAG_PASSWORD_INPUT_TYPE
-                        | FLAG_VIEW_NOT_FOCUSED);
+                        | FLAG_VIEW_NOT_FOCUSED
+                        | FLAG_ACTIVITY_START);
         this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
 
         onConstructed();
@@ -384,7 +394,7 @@
         byte flg = in.readByte();
         int id = in.readInt();
         List<FillContext> fillContexts = new ArrayList<>();
-        in.readParcelableList(fillContexts, FillContext.class.getClassLoader(), android.service.autofill.FillContext.class);
+        in.readParcelableList(fillContexts, FillContext.class.getClassLoader());
         Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
         int flags = in.readInt();
         InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
@@ -401,7 +411,8 @@
                 FLAG_MANUAL_REQUEST
                         | FLAG_COMPATIBILITY_MODE_REQUEST
                         | FLAG_PASSWORD_INPUT_TYPE
-                        | FLAG_VIEW_NOT_FOCUSED);
+                        | FLAG_VIEW_NOT_FOCUSED
+                        | FLAG_ACTIVITY_START);
         this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
 
         onConstructed();
@@ -422,10 +433,10 @@
     };
 
     @DataClass.Generated(
-            time = 1589280816805L,
-            codegenVersion = "1.0.15",
+            time = 1643052544776L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
-            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index d94988e..903e77f 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -79,11 +79,14 @@
     private final @Nullable RemoteViews mPresentation;
     private final @Nullable InlinePresentation mInlinePresentation;
     private final @Nullable InlinePresentation mInlineTooltipPresentation;
+    private final @Nullable RemoteViews mDialogPresentation;
+    private final @Nullable RemoteViews mDialogHeader;
     private final @Nullable RemoteViews mHeader;
     private final @Nullable RemoteViews mFooter;
     private final @Nullable IntentSender mAuthentication;
     private final @Nullable AutofillId[] mAuthenticationIds;
     private final @Nullable AutofillId[] mIgnoredIds;
+    private final @Nullable AutofillId[] mFillDialogTriggerIds;
     private final long mDisableDuration;
     private final @Nullable AutofillId[] mFieldClassificationIds;
     private final int mFlags;
@@ -99,10 +102,13 @@
         mPresentation = builder.mPresentation;
         mInlinePresentation = builder.mInlinePresentation;
         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
+        mDialogPresentation = builder.mDialogPresentation;
+        mDialogHeader = builder.mDialogHeader;
         mHeader = builder.mHeader;
         mFooter = builder.mFooter;
         mAuthentication = builder.mAuthentication;
         mAuthenticationIds = builder.mAuthenticationIds;
+        mFillDialogTriggerIds = builder.mFillDialogTriggerIds;
         mIgnoredIds = builder.mIgnoredIds;
         mDisableDuration = builder.mDisableDuration;
         mFieldClassificationIds = builder.mFieldClassificationIds;
@@ -144,6 +150,16 @@
     }
 
     /** @hide */
+    public @Nullable RemoteViews getDialogPresentation() {
+        return mDialogPresentation;
+    }
+
+    /** @hide */
+    public @Nullable RemoteViews getDialogHeader() {
+        return mDialogHeader;
+    }
+
+    /** @hide */
     public @Nullable RemoteViews getHeader() {
         return mHeader;
     }
@@ -164,6 +180,11 @@
     }
 
     /** @hide */
+    public @Nullable AutofillId[] getFillDialogTriggerIds() {
+        return mFillDialogTriggerIds;
+    }
+
+    /** @hide */
     public @Nullable AutofillId[] getIgnoredIds() {
         return mIgnoredIds;
     }
@@ -229,6 +250,8 @@
         private RemoteViews mPresentation;
         private InlinePresentation mInlinePresentation;
         private InlinePresentation mInlineTooltipPresentation;
+        private RemoteViews mDialogPresentation;
+        private RemoteViews mDialogHeader;
         private RemoteViews mHeader;
         private RemoteViews mFooter;
         private IntentSender mAuthentication;
@@ -236,6 +259,7 @@
         private AutofillId[] mIgnoredIds;
         private long mDisableDuration;
         private AutofillId[] mFieldClassificationIds;
+        private AutofillId[] mFillDialogTriggerIds;
         private int mFlags;
         private boolean mDestroyed;
         private UserData mUserData;
@@ -243,7 +267,7 @@
         private boolean mSupportsInlineSuggestions;
 
         /**
-         * Triggers a custom UI before before autofilling the screen with any data set in this
+         * Triggers a custom UI before autofilling the screen with any data set in this
          * response.
          *
          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
@@ -277,7 +301,7 @@
          * example a credit card whose CVV needs to be entered.
          *
          * <p>If you provide an authentication intent you must also provide a presentation
-         * which is used to visualize visualize the response for triggering the authentication
+         * which is used to visualize the response for triggering the authentication
          * flow.
          *
          * <p><b>Note:</b> Do not make the provided pending intent
@@ -306,7 +330,11 @@
          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
          *
          * @see android.app.PendingIntent#getIntentSender()
+         * @deprecated Use
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+         * instead.
          */
+        @Deprecated
         @NonNull
         public Builder setAuthentication(@NonNull AutofillId[] ids,
                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
@@ -327,7 +355,7 @@
         }
 
         /**
-         * Triggers a custom UI before before autofilling the screen with any data set in this
+         * Triggers a custom UI before autofilling the screen with any data set in this
          * response.
          *
          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
@@ -365,7 +393,11 @@
          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
          *
          * @see android.app.PendingIntent#getIntentSender()
+         * @deprecated Use
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+         * instead.
          */
+        @Deprecated
         @NonNull
         public Builder setAuthentication(@NonNull AutofillId[] ids,
                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
@@ -374,13 +406,18 @@
         }
 
         /**
-         * Triggers a custom UI before before autofilling the screen with any data set in this
+         * Triggers a custom UI before autofilling the screen with any data set in this
          * response.
          *
          * <p>This method like
          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)}
          * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip.
+         *
+         * @deprecated Use
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
+         * instead.
          */
+        @Deprecated
         @NonNull
         public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
@@ -388,6 +425,105 @@
                 @Nullable InlinePresentation inlineTooltipPresentation) {
             throwIfDestroyed();
             throwIfDisableAutofillCalled();
+            return setAuthentication(ids, authentication, presentation,
+                    inlinePresentation, inlineTooltipPresentation, null);
+        }
+
+        /**
+         * Triggers a custom UI before autofilling the screen with any data set in this
+         * response.
+         *
+         * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
+         * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
+         * for examples.
+         *
+         * <p>This is typically useful when a user interaction is required to unlock their
+         * data vault if you encrypt the data set labels and data set data. It is recommended
+         * to encrypt only the sensitive data and not the data set labels which would allow
+         * auth on the data set level leading to a better user experience. Note that if you
+         * use sensitive data as a label, for example an email address, then it should also
+         * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
+         * {@link Activity} which implements your authentication flow. Also if you provide an auth
+         * intent you also need to specify the presentation view to be shown in the fill UI
+         * for the user to trigger your authentication flow.
+         *
+         * <p>When a user triggers autofill, the system launches the provided intent
+         * whose extras will have the
+         * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
+         * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
+         * client state}. Once you complete your authentication flow you should set the
+         * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
+         * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
+         * with the fully populated {@link FillResponse response} (or {@code null} if the screen
+         * cannot be autofilled).
+         *
+         * <p>For example, if you provided an empty {@link FillResponse response} because the
+         * user's data was locked and marked that the response needs an authentication then
+         * in the response returned if authentication succeeds you need to provide all
+         * available data sets some of which may need to be further authenticated, for
+         * example a credit card whose CVV needs to be entered.
+         *
+         * <p>If you provide an authentication intent you must also provide a presentation
+         * which is used to visualize the response for triggering the authentication
+         * flow.
+         *
+         * <p><b>Note:</b> Do not make the provided pending intent
+         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+         * platform needs to fill in the authentication arguments.
+         *
+         * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
+         * not work with {@link InlinePresentation}.</p>
+         *
+         * @param ids id of Views that when focused will display the authentication UI.
+         * @param authentication Intent to an activity with your authentication flow.
+         * @param presentations The presentations to visualize the response.
+         *
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ul>
+         *   <li>{@code ids} is {@code null}</li>
+         *   <li>{@code ids} is empty</li>
+         *   <li>{@code ids} contains a {@code null} element</li>
+         *   <li>{@code authentication} is {@code null}, but either or both of
+         *   {@code presentations.getPresentation()} and
+         *   {@code presentations.getInlinePresentation()} is non-{@code null}</li>
+         *   <li>{@code authentication} is non-{{@code null}, but both
+         *   {@code presentations.getPresentation()} and
+         *   {@code presentations.getInlinePresentation()} are {@code null}</li>
+         * </ul>
+         *
+         * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
+         * {@link #setFooter(RemoteViews) footer} are already set for this builder.
+         *
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
+                @Nullable IntentSender authentication,
+                @Nullable Presentations presentations) {
+            throwIfDestroyed();
+            throwIfDisableAutofillCalled();
+            if (presentations == null) {
+                return setAuthentication(ids, authentication, null, null, null, null);
+            }
+            return setAuthentication(ids, authentication,
+                    presentations.getMenuPresentation(),
+                    presentations.getInlinePresentation(),
+                    presentations.getInlineTooltipPresentation(),
+                    presentations.getDialogPresentation());
+        }
+
+        /**
+         * Triggers a custom UI before autofilling the screen with any data set in this
+         * response.
+         */
+        @NonNull
+        private Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
+                @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
+                @Nullable InlinePresentation inlinePresentation,
+                @Nullable InlinePresentation inlineTooltipPresentation,
+                @Nullable RemoteViews dialogPresentation) {
+            throwIfDestroyed();
+            throwIfDisableAutofillCalled();
             if (mHeader != null || mFooter != null) {
                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
             }
@@ -400,6 +536,7 @@
             mPresentation = presentation;
             mInlinePresentation = inlinePresentation;
             mInlineTooltipPresentation = inlineTooltipPresentation;
+            mDialogPresentation = dialogPresentation;
             mAuthenticationIds = assertValid(ids);
             return this;
         }
@@ -552,7 +689,7 @@
          *
          * @throws IllegalArgumentException if {@code duration} is not a positive number.
          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
-         *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
+         *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
          *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
          */
@@ -591,8 +728,8 @@
          * @return this builder
          *
          * @throws IllegalStateException if an
-         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was
-         * already set for this builder.
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
+         * authentication} was already set for this builder.
          */
         // TODO(b/69796626): make it sticky / update javadoc
         @NonNull
@@ -623,7 +760,7 @@
          * @return this builder
          *
          * @throws IllegalStateException if the FillResponse
-         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
          * requires authentication}.
          */
         // TODO(b/69796626): make it sticky / update javadoc
@@ -643,7 +780,7 @@
          *
          * @return this builder
          * @throws IllegalStateException if the FillResponse
-         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
          * requires authentication}.
          */
         @NonNull
@@ -674,13 +811,46 @@
         }
 
         /**
+         * Sets the presentation of header in fill dialog UI. The header should have
+         * a prompt for what datasets are shown in the dialog. If this is not set,
+         * the dialog only shows your application icon.
+         *
+         * More details about the fill dialog, see
+         * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+         */
+        @NonNull
+        public Builder setDialogHeader(@NonNull RemoteViews header) {
+            throwIfDestroyed();
+            Objects.requireNonNull(header);
+            mDialogHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets which fields are used for the fill dialog UI.
+         *
+         * More details about the fill dialog, see
+         * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+         *
+         * @throws IllegalStateException if {@link #build()} was already called.
+         * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
+         */
+        @NonNull
+        public Builder setFillDialogTriggerIds(@NonNull AutofillId... ids) {
+            throwIfDestroyed();
+            Preconditions.checkArrayElementsNotNull(ids, "ids");
+            mFillDialogTriggerIds = ids;
+            return this;
+        }
+
+        /**
          * Builds a new {@link FillResponse} instance.
          *
          * @throws IllegalStateException if any of the following conditions occur:
          * <ol>
          *   <li>{@link #build()} was already called.
          *   <li>No call was made to {@link #addDataset(Dataset)},
-         *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
+         *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
          *       {@link #setClientState(Bundle)},
          *       or {@link #setFieldClassificationIds(AutofillId...)}.
@@ -767,6 +937,12 @@
         if (mInlineTooltipPresentation != null) {
             builder.append(", hasInlineTooltipPresentation");
         }
+        if (mDialogPresentation != null) {
+            builder.append(", hasDialogPresentation");
+        }
+        if (mDialogHeader != null) {
+            builder.append(", hasDialogHeader");
+        }
         if (mHeader != null) {
             builder.append(", hasHeader");
         }
@@ -779,6 +955,10 @@
         if (mAuthenticationIds != null) {
             builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
         }
+        if (mFillDialogTriggerIds != null) {
+            builder.append(", fillDialogTriggerIds=")
+                    .append(Arrays.toString(mFillDialogTriggerIds));
+        }
         builder.append(", disableDuration=").append(mDisableDuration);
         if (mFlags != 0) {
             builder.append(", flags=").append(mFlags);
@@ -815,6 +995,9 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeParcelable(mInlinePresentation, flags);
         parcel.writeParcelable(mInlineTooltipPresentation, flags);
+        parcel.writeParcelable(mDialogPresentation, flags);
+        parcel.writeParcelable(mDialogHeader, flags);
+        parcel.writeParcelableArray(mFillDialogTriggerIds, flags);
         parcel.writeParcelable(mHeader, flags);
         parcel.writeParcelable(mFooter, flags);
         parcel.writeParcelable(mUserData, flags);
@@ -850,9 +1033,18 @@
             final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
             final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
             final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
+            final RemoteViews dialogPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
             if (authenticationIds != null) {
                 builder.setAuthentication(authenticationIds, authentication, presentation,
-                        inlinePresentation, inlineTooltipPresentation);
+                        inlinePresentation, inlineTooltipPresentation, dialogPresentation);
+            }
+            final RemoteViews dialogHeader = parcel.readParcelable(null, android.widget.RemoteViews.class);
+            if (dialogHeader != null) {
+                builder.setDialogHeader(dialogHeader);
+            }
+            final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class);
+            if (triggerIds != null) {
+                builder.setFillDialogTriggerIds(triggerIds);
             }
             final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class);
             if (header != null) {
diff --git a/core/java/android/service/autofill/Presentations.java b/core/java/android/service/autofill/Presentations.java
new file mode 100644
index 0000000..e8ac628
--- /dev/null
+++ b/core/java/android/service/autofill/Presentations.java
@@ -0,0 +1,278 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Holds presentations used to visualize autofill suggestions for each available UI type.
+ *
+ * @see Field
+ */
+@DataClass(genBuilder = true)
+public final class Presentations {
+
+    /**
+     * The presentation used to visualize this field in fill UI.
+     *
+     * <p>Note: Before Android 13, this was referred to simply as "presentation" in the SDK.
+     *
+     * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+     * or background color: Autofill on different platforms may have different themes.
+     */
+    private @Nullable RemoteViews mMenuPresentation;
+
+    /**
+     * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+     * If the dataset supports inline suggestions, this should not be null.
+     */
+    private @Nullable InlinePresentation mInlinePresentation;
+
+    /**
+     * The presentation used to visualize this field in the
+     * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
+     *
+     * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+     * or background color: Autofill on different platforms may have different themes.
+     */
+    private @Nullable RemoteViews mDialogPresentation;
+
+    /**
+     * The {@link InlinePresentation} used to show the tooltip for the
+     * {@code mInlinePresentation}. If the set this field, the
+     * {@code mInlinePresentation} should not be null.
+     */
+    private @Nullable InlinePresentation mInlineTooltipPresentation;
+
+    private static RemoteViews defaultMenuPresentation() {
+        return null;
+    }
+
+    private static InlinePresentation defaultInlinePresentation() {
+        return null;
+    }
+
+    private static RemoteViews defaultDialogPresentation() {
+        return null;
+    }
+
+    private static InlinePresentation defaultInlineTooltipPresentation() {
+        return null;
+    }
+
+    private void onConstructed() {
+        if (mMenuPresentation == null
+                && mInlinePresentation == null
+                && mDialogPresentation == null) {
+            throw new IllegalStateException("All presentations are null.");
+        }
+        if (mInlineTooltipPresentation != null && mInlinePresentation == null) {
+            throw new IllegalStateException(
+                    "The inline presentation is required for mInlineTooltipPresentation.");
+        }
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/service/autofill/Presentations.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ Presentations(
+            @Nullable RemoteViews menuPresentation,
+            @Nullable InlinePresentation inlinePresentation,
+            @Nullable RemoteViews dialogPresentation,
+            @Nullable InlinePresentation inlineTooltipPresentation) {
+        this.mMenuPresentation = menuPresentation;
+        this.mInlinePresentation = inlinePresentation;
+        this.mDialogPresentation = dialogPresentation;
+        this.mInlineTooltipPresentation = inlineTooltipPresentation;
+
+        onConstructed();
+    }
+
+    /**
+     * The presentation used to visualize this field in fill UI.
+     *
+     * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+     * or background color: Autofill on different platforms may have different themes.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RemoteViews getMenuPresentation() {
+        return mMenuPresentation;
+    }
+
+    /**
+     * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+     * If the dataset supports inline suggestions, this should not be null.
+     */
+    @DataClass.Generated.Member
+    public @Nullable InlinePresentation getInlinePresentation() {
+        return mInlinePresentation;
+    }
+
+    /**
+     * The presentation used to visualize this field in the fill dialog UI.
+     *
+     * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+     * or background color: Autofill on different platforms may have different themes.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RemoteViews getDialogPresentation() {
+        return mDialogPresentation;
+    }
+
+    /**
+     * The {@link InlinePresentation} used to show the tooltip for the
+     * {@code mInlinePresentation}. If the set this field, the
+     * {@code mInlinePresentation} should not be null.
+     */
+    @DataClass.Generated.Member
+    public @Nullable InlinePresentation getInlineTooltipPresentation() {
+        return mInlineTooltipPresentation;
+    }
+
+    /**
+     * A builder for {@link Presentations}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable RemoteViews mMenuPresentation;
+        private @Nullable InlinePresentation mInlinePresentation;
+        private @Nullable RemoteViews mDialogPresentation;
+        private @Nullable InlinePresentation mInlineTooltipPresentation;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The presentation used to visualize this field in fill UI.
+         *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMenuPresentation(@NonNull RemoteViews value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mMenuPresentation = value;
+            return this;
+        }
+
+        /**
+         * The {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+         * If the dataset supports inline suggestions, this should not be null.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setInlinePresentation(@NonNull InlinePresentation value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mInlinePresentation = value;
+            return this;
+        }
+
+        /**
+         * The presentation used to visualize this field in the fill dialog UI.
+         *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDialogPresentation(@NonNull RemoteViews value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mDialogPresentation = value;
+            return this;
+        }
+
+        /**
+         * The {@link InlinePresentation} used to show the tooltip for the
+         * {@code mInlinePresentation}. If the set this field, the
+         * {@code mInlinePresentation} should not be null.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setInlineTooltipPresentation(@NonNull InlinePresentation value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mInlineTooltipPresentation = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull Presentations build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mMenuPresentation = defaultMenuPresentation();
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mInlinePresentation = defaultInlinePresentation();
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mDialogPresentation = defaultDialogPresentation();
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mInlineTooltipPresentation = defaultInlineTooltipPresentation();
+            }
+            Presentations o = new Presentations(
+                    mMenuPresentation,
+                    mInlinePresentation,
+                    mDialogPresentation,
+                    mInlineTooltipPresentation);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1643083242164L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/Presentations.java",
+            inputSignatures = "private @android.annotation.Nullable android.widget.RemoteViews mMenuPresentation\nprivate @android.annotation.Nullable android.service.autofill.InlinePresentation mInlinePresentation\nprivate @android.annotation.Nullable android.widget.RemoteViews mDialogPresentation\nprivate @android.annotation.Nullable android.service.autofill.InlinePresentation mInlineTooltipPresentation\nprivate static  android.widget.RemoteViews defaultMenuPresentation()\nprivate static  android.service.autofill.InlinePresentation defaultInlinePresentation()\nprivate static  android.widget.RemoteViews defaultDialogPresentation()\nprivate static  android.service.autofill.InlinePresentation defaultInlineTooltipPresentation()\nprivate  void onConstructed()\nclass Presentations extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
index 31352f1..11e5ad8 100644
--- a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -37,5 +37,6 @@
     boolean getOemUnlockEnabled();
     int getFlashLockState();
     boolean hasFrpCredentialHandle();
+    String getPersistentDataPackageName();
 }
 
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 44a8862..9167153 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -26,8 +26,6 @@
 import android.os.RemoteException;
 import android.service.oemlock.OemLockManager;
 
-import com.android.internal.R;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -53,7 +51,6 @@
 @SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
 public class PersistentDataBlockManager {
     private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
-    private final Context mContext;
     private IPersistentDataBlockService sService;
 
     /**
@@ -78,10 +75,7 @@
     public @interface FlashLockState {}
 
     /** @hide */
-    public PersistentDataBlockManager(
-            Context context,
-            IPersistentDataBlockService service) {
-        mContext = context;
+    public PersistentDataBlockManager(IPersistentDataBlockService service) {
         sService = service;
     }
 
@@ -219,7 +213,12 @@
      */
     @SystemApi
     @NonNull
+    @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE)
     public String getPersistentDataPackageName() {
-        return mContext.getString(R.string.config_persistentDataPackageName);
+        try {
+            return sService.getPersistentDataPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 }
diff --git a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
index c452e06..08a7205 100644
--- a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -16,12 +16,21 @@
 
 package android.service.selectiontoolbar;
 
-import android.util.Log;
+import static android.view.selectiontoolbar.SelectionToolbarManager.ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR;
+import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID;
+
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
 import android.view.selectiontoolbar.ShowInfo;
 
+import java.util.UUID;
+
 /**
  * The default implementation of {@link SelectionToolbarRenderService}.
  *
+ * <p><b>NOTE:<b/> The requests are handled on the service main thread.
+ *
  *  @hide
  */
 // TODO(b/214122495): fix class not found then move to system service folder
@@ -29,22 +38,97 @@
 
     private static final String TAG = "DefaultSelectionToolbarRenderService";
 
+    // TODO(b/215497659): handle remove if the client process dies.
+    // Only show one toolbar, dismiss the old ones and remove from cache
+    private final SparseArray<Pair<Long, RemoteSelectionToolbar>> mToolbarCache =
+            new SparseArray<>();
+
+    /**
+     * Only allow one package to create one toolbar.
+     */
+    private boolean canShowToolbar(int uid, ShowInfo showInfo) {
+        if (showInfo.getWidgetToken() != NO_TOOLBAR_ID) {
+            return true;
+        }
+        return mToolbarCache.indexOfKey(uid) < 0;
+    }
+
     @Override
-    public void onShow(ShowInfo showInfo,
+    public void onShow(int callingUid, ShowInfo showInfo,
             SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
-        // TODO: Add implementation
-        Log.w(TAG, "onShow()");
+        if (!canShowToolbar(callingUid, showInfo)) {
+            Slog.e(TAG, "Do not allow multiple toolbar for the app.");
+            callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR);
+            return;
+        }
+        long widgetToken = showInfo.getWidgetToken() == NO_TOOLBAR_ID
+                ? UUID.randomUUID().getMostSignificantBits()
+                : showInfo.getWidgetToken();
+
+        if (mToolbarCache.indexOfKey(callingUid) < 0) {
+            RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(this,
+                    widgetToken, showInfo.getHostInputToken(),
+                    callbackWrapper, this::transferTouch);
+            mToolbarCache.put(callingUid, new Pair<>(widgetToken, toolbar));
+        }
+        Slog.v(TAG, "onShow() for " + widgetToken);
+        Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid);
+        if (toolbarPair.first == widgetToken) {
+            toolbarPair.second.show(showInfo);
+        } else {
+            Slog.w(TAG, "onShow() for unknown " + widgetToken);
+        }
     }
 
     @Override
     public void onHide(long widgetToken) {
-        // TODO: Add implementation
-        Log.w(TAG, "onHide()");
+        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken);
+        if (toolbar != null) {
+            Slog.v(TAG, "onHide() for " + widgetToken);
+            toolbar.hide(widgetToken);
+        }
     }
 
     @Override
     public void onDismiss(long widgetToken) {
-        // TODO: Add implementation
-        Log.w(TAG, "onDismiss()");
+        RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken);
+        if (toolbar != null) {
+            Slog.v(TAG, "onDismiss() for " + widgetToken);
+            toolbar.dismiss(widgetToken);
+            removeRemoteSelectionToolbarByTokenLocked(widgetToken);
+        }
+    }
+
+    @Override
+    public void onToolbarShowTimeout(int callingUid) {
+        Slog.w(TAG, "onToolbarShowTimeout for callingUid = " + callingUid);
+        Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.get(callingUid);
+        if (toolbarPair != null) {
+            RemoteSelectionToolbar remoteToolbar = toolbarPair.second;
+            remoteToolbar.dismiss(toolbarPair.first);
+            remoteToolbar.onToolbarShowTimeout();
+            mToolbarCache.remove(callingUid);
+        }
+    }
+
+    private RemoteSelectionToolbar getRemoteSelectionToolbarByTokenLocked(long widgetToken) {
+        for (int i = 0; i < mToolbarCache.size(); i++) {
+            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
+            if (toolbarPair.first == widgetToken) {
+                return toolbarPair.second;
+            }
+        }
+        return null;
+    }
+
+    private void removeRemoteSelectionToolbarByTokenLocked(long widgetToken) {
+        for (int i = 0; i < mToolbarCache.size(); i++) {
+            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
+            if (toolbarPair.first == widgetToken) {
+                mToolbarCache.remove(mToolbarCache.keyAt(i));
+                return;
+            }
+        }
     }
 }
+
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
new file mode 100644
index 0000000..04491f0
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -0,0 +1,78 @@
+/*
+ * 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.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+/**
+ * This class is the root view for the selection toolbar. It is responsible for
+ * detecting the click on the item and to also transfer input focus to the application.
+ *
+ * @hide
+ */
+@SuppressLint("ViewConstructor")
+public class FloatingToolbarRoot extends LinearLayout {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "FloatingToolbarRoot";
+
+    private final IBinder mTargetInputToken;
+    private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
+    private Rect mContentRect;
+
+    public FloatingToolbarRoot(Context context, IBinder targetInputToken,
+            SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
+        super(context);
+        mTargetInputToken = targetInputToken;
+        mTransferTouchListener = transferTouchListener;
+        setFocusable(false);
+    }
+
+    /**
+     * Sets the Rect that shows the selection toolbar content.
+     */
+    public void setContentRect(Rect contentRect) {
+        mContentRect = contentRect;
+    }
+
+    @Override
+    @SuppressLint("ClickableViewAccessibility")
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            int downX = (int) event.getX();
+            int downY = (int) event.getY();
+            if (DEBUG) {
+                Log.d(TAG, "downX=" + downX + " downY=" + downY);
+            }
+            // TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+            if (!mContentRect.contains(downX, downY)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Transfer touch focus to application.");
+                }
+                mTransferTouchListener.onTransferTouch(getViewRootImpl().getInputToken(),
+                        mTargetInputToken);
+            }
+        }
+        return super.dispatchTouchEvent(event);
+    }
+}
diff --git a/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
index 2bd99ac..79281b8 100644
--- a/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
+++ b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
@@ -25,7 +25,8 @@
  * @hide
  */
 oneway interface ISelectionToolbarRenderService {
-    void onShow(in ShowInfo showInfo, in ISelectionToolbarCallback callback);
+    void onConnected(in IBinder callback);
+    void onShow(int callingUid, in ShowInfo showInfo, in ISelectionToolbarCallback callback);
     void onHide(long widgetToken);
-    void onDismiss(long widgetToken);
+    void onDismiss(int callingUid, long widgetToken);
 }
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderServiceCallback.aidl
similarity index 64%
copy from core/java/android/view/selectiontoolbar/SelectionContext.aidl
copy to core/java/android/service/selectiontoolbar/ISelectionToolbarRenderServiceCallback.aidl
index 5206831..f6c47dd 100644
--- a/core/java/android/view/selectiontoolbar/SelectionContext.aidl
+++ b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderServiceCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,9 +14,15 @@
  * limitations under the License.
  */
 
-package android.view.selectiontoolbar;
+package android.service.selectiontoolbar;
+
+import android.os.IBinder;
 
 /**
+ * The interface from the SelectionToolbarRenderService to the system.
+ *
  * @hide
  */
-parcelable SelectionContext;
+oneway interface ISelectionToolbarRenderServiceCallback {
+    void transferTouch(in IBinder source, in IBinder target);
+}
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 95ecc4e..179d39d 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -21,72 +21,63 @@
 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.os.IBinder;
 import android.text.TextUtils;
+import android.util.Log;
 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.SurfaceControlViewHost;
 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.view.selectiontoolbar.ShowInfo;
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
 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 com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
-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 rendering/animation of the selection toolbar in the remote
+ * system process. It holds 2 panels (i.e. main panel and overflow panel) and an overflow
+ * button to transition between panels.
  *
- * 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.
+ *  @hide
  */
-
-final class RemoteSelectionToolbar implements FloatingToolbarPopup {
+// TODO(b/215497659): share code with LocalFloatingToolbarPopup
+final class RemoteSelectionToolbar {
+    private static final String TAG = "RemoteSelectionToolbar";
 
     /* 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;
@@ -121,23 +112,22 @@
     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;
 
+    private final long mSelectionToolbarToken;
+    private IBinder mHostInputToken;
+    private final SelectionToolbarRenderService.RemoteCallbackWrapper mCallbackWrapper;
+    private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
+    private int mPopupWidth;
+    private int mPopupHeight;
+    // Coordinates to show the toolbar relative to the specified view port
+    private final Point mRelativeCoordsForToolbar = new Point();
+    private List<ToolbarMenuItem> mMenuItems;
+    private SurfaceControlViewHost mSurfaceControlViewHost;
+    private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
     /**
      * @see OverflowPanelViewHelper#preparePopupContent().
      */
@@ -145,7 +135,6 @@
         @Override
         public void run() {
             setPanelsStatesAtRestingPosition();
-            setContentAreaAsTouchableSurface();
             mContentContainer.setAlpha(1);
         }
     };
@@ -159,26 +148,7 @@
     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 final View.OnClickListener mMenuItemButtonOnClickListener;
 
     private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
     private boolean mIsOverflowOpen;
@@ -186,27 +156,28 @@
     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);
+    private final Rect mTempContentRect = new Rect();
+    private final Rect mTempContentRectForRoot = new Rect();
+    private final int[] mTempCoords = new int[2];
+
+    RemoteSelectionToolbar(Context context, long selectionToolbarToken, IBinder hostInputToken,
+            SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper,
+            SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
         mContext = applyDefaultTheme(context);
+        mSelectionToolbarToken = selectionToolbarToken;
+        mCallbackWrapper = callbackWrapper;
+        mTransferTouchListener = transferTouchListener;
+        mHostInputToken = hostInputToken;
+
         mContentContainer = createContentContainer(mContext);
-        mPopupWindow = createPopupWindow(mContentContainer);
-        mMarginHorizontal = parent.getResources()
+        mMarginHorizontal = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-        mMarginVertical = parent.getResources()
+        mMarginVertical = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
-        mLineHeight = context.getResources()
+        mLineHeight = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.floating_toolbar_height);
-        mIconTextSpacing = context.getResources()
+        mIconTextSpacing = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
 
         // Interpolators
@@ -245,53 +216,81 @@
         mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
         mCloseOverflowAnimation = new AnimationSet(true);
         mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
-        mShowAnimation = createEnterAnimation(mContentContainer);
+        mShowAnimation = createEnterAnimation(mContentContainer,
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        updateFloatingToolbarRootContentRect();
+                    }
+                });
         mDismissAnimation = createExitAnimation(
                 mContentContainer,
                 150,  // startDelay
                 new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
-                        mPopupWindow.dismiss();
+                        // TODO(b/215497659): should dismiss window after animation
                         mContentContainer.removeAllViews();
+                        mSurfaceControlViewHost.release();
+                        mSurfaceControlViewHost = null;
+                        mSurfacePackage = null;
                     }
                 });
         mHideAnimation = createExitAnimation(
                 mContentContainer,
                 0,  // startDelay
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mPopupWindow.dismiss();
-                    }
-                });
+                null); // TODO(b/215497659): should handle hide after animation
+        mMenuItemButtonOnClickListener = v -> {
+            Object tag = v.getTag();
+            if (!(tag instanceof ToolbarMenuItem)) {
+                return;
+            }
+            mCallbackWrapper.onMenuItemClicked((ToolbarMenuItem) tag);
+        };
     }
 
-    @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;
+    private void updateFloatingToolbarRootContentRect() {
+        if (mSurfaceControlViewHost == null) {
+            return;
         }
-        mPopupWindow.setOnDismissListener(onDismiss);
-        return ret;
+        final FloatingToolbarRoot root = (FloatingToolbarRoot) mSurfaceControlViewHost.getView();
+        mContentContainer.getLocationOnScreen(mTempCoords);
+        int contentLeft = mTempCoords[0];
+        int contentTop = mTempCoords[1];
+        mTempContentRectForRoot.set(contentLeft, contentTop,
+                contentLeft + mContentContainer.getWidth(),
+                contentTop + mContentContainer.getHeight());
+        root.setContentRect(mTempContentRectForRoot);
     }
 
-    /**
-     * Lays out buttons for the specified menu items.
-     * Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
-     */
+    private WidgetInfo createWidgetInfo() {
+        mTempContentRect.set(mRelativeCoordsForToolbar.x, mRelativeCoordsForToolbar.y,
+                mRelativeCoordsForToolbar.x + mPopupWidth,
+                mRelativeCoordsForToolbar.y + mPopupHeight);
+        return new WidgetInfo(mSelectionToolbarToken, mTempContentRect, getSurfacePackage());
+    }
+
+    private SurfaceControlViewHost.SurfacePackage getSurfacePackage() {
+        if (mSurfaceControlViewHost == null) {
+            final FloatingToolbarRoot contentHolder = new FloatingToolbarRoot(mContext,
+                    mHostInputToken, mTransferTouchListener);
+            contentHolder.addView(mContentContainer);
+            mSurfaceControlViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+                    mHostInputToken);
+            mSurfaceControlViewHost.setView(contentHolder, mPopupWidth, mPopupHeight);
+        }
+        if (mSurfacePackage == null) {
+            mSurfacePackage = mSurfaceControlViewHost.getSurfacePackage();
+        }
+        return mSurfacePackage;
+    }
+
     private void layoutMenuItems(
-            List<MenuItem> menuItems,
-            MenuItem.OnMenuItemClickListener menuItemClickListener,
+            List<ToolbarMenuItem> menuItems,
             int suggestedWidth) {
         cancelOverflowAnimations();
         clearPanels();
-        updateMenuItems(menuItems, menuItemClickListener);
+
         menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
         if (!menuItems.isEmpty()) {
             // Add remaining items to the overflow.
@@ -300,148 +299,98 @@
         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;
+    public void onToolbarShowTimeout() {
+        mCallbackWrapper.onToolbarShowTimeout();
     }
 
     /**
-     * Returns true if this popup needs a relayout to properly render the specified menu items.
+     * Show the specified selection toolbar.
      */
-    private boolean isLayoutRequired(List<MenuItem> menuItems) {
-        return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
-    }
+    public void show(ShowInfo showInfo) {
+        debugLog("show() for " + showInfo);
 
-    @Override
-    public void setWidthChanged(boolean widthChanged) {
-        mWidthChanged = widthChanged;
-    }
+        mMenuItems = showInfo.getMenuItems();
+        mViewPortOnScreen.set(showInfo.getViewPortOnScreen());
 
-    @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);
+        debugLog("show(): layoutRequired=" + showInfo.isLayoutRequired());
+        if (showInfo.isLayoutRequired()) {
+            layoutMenuItems(mMenuItems, showInfo.getSuggestedWidth());
         }
+        Rect contentRect = showInfo.getContentRect();
         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();
+        mCallbackWrapper.onShown(createWidgetInfo());
+        // TODO(b/215681595): Use Choreographer to coordinate for show between different thread
+        mShowAnimation.start();
     }
 
-    @Override
-    public void dismiss() {
+    /**
+     * Dismiss the specified selection toolbar.
+     */
+    public void dismiss(long floatingToolbarToken) {
+        debugLog("dismiss for " + floatingToolbarToken);
         if (mDismissed) {
             return;
         }
-
         mHidden = false;
         mDismissed = true;
-        mHideAnimation.cancel();
 
-        runDismissAnimation();
-        setZeroTouchableSurface();
+        mHideAnimation.cancel();
+        mDismissAnimation.start();
     }
 
-    @Override
-    public void hide() {
+    /**
+     * Hide the specified selection toolbar.
+     */
+    public void hide(long floatingToolbarToken) {
+        debugLog("hide for " + floatingToolbarToken);
         if (!isShowing()) {
             return;
         }
 
         mHidden = true;
-        runHideAnimation();
-        setZeroTouchableSurface();
+        mHideAnimation.start();
     }
 
-    @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()) {
+        if (!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());
+        WidgetInfo widgetInfo = createWidgetInfo();
+        mSurfaceControlViewHost.relayout(mPopupWidth, mPopupHeight);
+        mCallbackWrapper.onWidgetUpdated(widgetInfo);
     }
 
     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());
+                contentRectOnScreen.centerX() - mPopupWidth / 2,
+                mViewPortOnScreen.right - mPopupWidth);
 
         final int y;
 
@@ -484,7 +433,7 @@
                 // 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();
+                y = contentRectOnScreen.top - mPopupHeight;
                 mOpenOverflowUpwards = true;
             } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
                     && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
@@ -507,7 +456,7 @@
                 // Position below but open upwards.
                 updateOverflowHeight(availableHeightThroughContentUp - margin);
                 y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin
-                        - mPopupWindow.getHeight();
+                        - mPopupHeight;
                 mOpenOverflowUpwards = true;
             } else {
                 // Not enough space.
@@ -517,45 +466,7 @@
                 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();
+        mRelativeCoordsForToolbar.set(x, y);
     }
 
     private void cancelDismissAndHideAnimations() {
@@ -625,15 +536,15 @@
                         isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
                 float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
                 mOverflowButton.setX(actualOverflowButtonX);
+                updateFloatingToolbarRootContentRect();
             }
         };
         widthAnimation.setInterpolator(mLogAccelerateInterpolator);
-        widthAnimation.setDuration(getAdjustedDuration(250));
+        widthAnimation.setDuration(getAnimationDuration());
         heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        heightAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setDuration(getAnimationDuration());
         overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
-        mOpenOverflowAnimation.getAnimations().clear();
+        overflowButtonAnimation.setDuration(getAnimationDuration());
         mOpenOverflowAnimation.getAnimations().clear();
         mOpenOverflowAnimation.addAnimation(widthAnimation);
         mOpenOverflowAnimation.addAnimation(heightAnimation);
@@ -701,14 +612,15 @@
                         isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
                 float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
                 mOverflowButton.setX(actualOverflowButtonX);
+                updateFloatingToolbarRootContentRect();
             }
         };
         widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        widthAnimation.setDuration(getAdjustedDuration(250));
+        widthAnimation.setDuration(getAnimationDuration());
         heightAnimation.setInterpolator(mLogAccelerateInterpolator);
-        heightAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setDuration(getAnimationDuration());
         overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setDuration(getAnimationDuration());
         mCloseOverflowAnimation.getAnimations().clear();
         mCloseOverflowAnimation.addAnimation(widthAnimation);
         mCloseOverflowAnimation.addAnimation(heightAnimation);
@@ -756,7 +668,7 @@
                 mOverflowPanel.setX(0);  // align left
             } else {
                 mContentContainer.setX(// align right
-                        mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                        mPopupWidth - containerSize.getWidth() - mMarginHorizontal);
                 mMainPanel.setX(-mContentContainer.getX());  // align right
                 mOverflowButton.setX(0);  // align left
                 mOverflowPanel.setX(0);  // align left
@@ -798,7 +710,7 @@
                     mOverflowPanel.setX(0);  // align left
                 } else {
                     mContentContainer.setX(// align right
-                            mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                            mPopupWidth - containerSize.getWidth() - mMarginHorizontal);
                     mMainPanel.setX(0);  // align left
                     mOverflowButton.setX(// align right
                             containerSize.getWidth() - mOverflowButtonSize.getWidth());
@@ -866,70 +778,23 @@
             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);
+        mPopupWidth = width + mMarginHorizontal * 2;
+        mPopupHeight = height + mMarginVertical * 2;
+        maybeComputeTransitionDurationScale();
     }
 
     private int getAdjustedToolbarWidth(int suggestedWidth) {
         int width = suggestedWidth;
-        refreshViewPort();
-        int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
+        int maximumWidth = mViewPortOnScreen.width() - 2 * mContext.getResources()
                 .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
         if (width <= 0) {
-            width = mParent.getResources()
+            width = mContext.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()
@@ -946,18 +811,14 @@
      *
      * @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<>();
+    private List<ToolbarMenuItem> layoutMainPanelItems(List<ToolbarMenuItem> menuItems,
+            int toolbarWidth) {
+        final LinkedList<ToolbarMenuItem> 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) {
+        final LinkedList<ToolbarMenuItem> overflowMenuItems = new LinkedList();
+        for (ToolbarMenuItem menuItem : menuItems) {
             if (menuItem.getItemId() != android.R.id.textAssist
-                    && menuItem.requiresOverflow()) {
+                    && menuItem.getPriority() == ToolbarMenuItem.PRIORITY_OVERFLOW) {
                 overflowMenuItems.add(menuItem);
             } else {
                 remainingMenuItems.add(menuItem);
@@ -968,25 +829,22 @@
         mMainPanel.removeAllViews();
         mMainPanel.setPaddingRelative(0, 0, 0, 0);
 
-        int lastGroupId = -1;
+        int availableWidth = toolbarWidth;
         boolean isFirstItem = true;
         while (!remainingMenuItems.isEmpty()) {
-            final MenuItem menuItem = remainingMenuItems.peek();
-
+            ToolbarMenuItem 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()) {
+            if (!isFirstItem && menuItem.getPriority() == ToolbarMenuItem.PRIORITY_OVERFLOW) {
                 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(
@@ -995,7 +853,6 @@
                         menuItemButton.getPaddingEnd(),
                         menuItemButton.getPaddingBottom());
             }
-
             // Adding additional end padding for the last button to even out button spacing.
             boolean isLastItem = remainingMenuItems.size() == 1;
             if (isLastItem) {
@@ -1005,18 +862,17 @@
                         (int) (1.5 * menuItemButton.getPaddingEnd()),
                         menuItemButton.getPaddingBottom());
             }
-
-            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            menuItemButton.measure(View.MeasureSpec.UNSPECIFIED, View.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);
+                menuItemButton.setTag(menuItem);
+                menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
                 // Set tooltips for main panel items, but not overflow items (b/35726766).
                 menuItemButton.setTooltipText(menuItem.getTooltipText());
                 mMainPanel.addView(menuItemButton);
@@ -1028,22 +884,20 @@
             } 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();
+    private void layoutOverflowPanelItems(List<ToolbarMenuItem> menuItems) {
+        ArrayAdapter<ToolbarMenuItem> overflowPanelAdapter =
+                (ArrayAdapter<ToolbarMenuItem>) mOverflowPanel.getAdapter();
         overflowPanelAdapter.clear();
         final int size = menuItems.size();
         for (int i = 0; i < size; i++) {
@@ -1055,7 +909,6 @@
         } else {
             mOverflowPanel.setY(mOverflowButtonSize.getHeight());
         }
-
         int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
         int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
         mOverflowPanelSize = new Size(width, height);
@@ -1067,7 +920,6 @@
      */
     private void preparePopupContent() {
         mContentContainer.removeAllViews();
-
         // Add views in the specified order so they stack up as expected.
         // Order: overflowPanel, mainPanel, overflowButton.
         if (hasOverflow()) {
@@ -1078,7 +930,6 @@
             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.
@@ -1093,12 +944,12 @@
      * Clears out the panels and their container. Resets their calculated sizes.
      */
     private void clearPanels() {
-        mOverflowPanelSize = null;
-        mMainPanelSize = null;
         mIsOverflowOpen = false;
+        mMainPanelSize = null;
         mMainPanel.removeAllViews();
-        ArrayAdapter<MenuItem> overflowPanelAdapter =
-                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        mOverflowPanelSize = null;
+        ArrayAdapter<ToolbarMenuItem> overflowPanelAdapter =
+                (ArrayAdapter<ToolbarMenuItem>) mOverflowPanel.getAdapter();
         overflowPanelAdapter.clear();
         mOverflowPanel.setAdapter(overflowPanelAdapter);
         mContentContainer.removeAllViews();
@@ -1116,7 +967,7 @@
         int overflowWidth = 0;
         final int count = mOverflowPanel.getAdapter().getCount();
         for (int i = 0; i < count; i++) {
-            MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
+            ToolbarMenuItem menuItem = (ToolbarMenuItem) mOverflowPanel.getAdapter().getItem(i);
             overflowWidth =
                     Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
         }
@@ -1141,29 +992,24 @@
                 + 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) {
+    private int getAnimationDuration() {
         if (mTransitionDurationScale < 150) {
             // For smaller transition, decrease the time.
-            return Math.max(originalDuration - 50, 0);
+            return 200;
         } else if (mTransitionDurationScale > 300) {
             // For bigger transition, increase the time.
-            return originalDuration + 50;
+            return 300;
         }
 
         // 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());
+        return (int) (250 * ValueAnimator.getDurationScale());
     }
 
     private void maybeComputeTransitionDurationScale() {
@@ -1176,7 +1022,7 @@
     }
 
     private ViewGroup createMainPanel() {
-        ViewGroup mainPanel = new LinearLayout(mContext) {
+        return new LinearLayout(mContext) {
             @Override
             protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                 if (isOverflowAnimating()) {
@@ -1195,7 +1041,6 @@
                 return isOverflowAnimating();
             }
         };
-        return mainPanel;
     }
 
     private ImageButton createOverflowButton() {
@@ -1203,6 +1048,12 @@
                 .inflate(R.layout.floating_popup_overflow_button, null);
         overflowButton.setImageDrawable(mOverflow);
         overflowButton.setOnClickListener(v -> {
+            if (isShowing()) {
+                preparePopupContent();
+                WidgetInfo widgetInfo = createWidgetInfo();
+                mSurfaceControlViewHost.relayout(mPopupWidth, mPopupHeight);
+                mCallbackWrapper.onWidgetUpdated(widgetInfo);
+            }
             if (mIsOverflowOpen) {
                 overflowButton.setImageDrawable(mToOverflow);
                 mToOverflow.start();
@@ -1224,7 +1075,7 @@
         overflowPanel.setDividerHeight(0);
 
         final ArrayAdapter adapter =
-                new ArrayAdapter<MenuItem>(mContext, 0) {
+                new ArrayAdapter<ToolbarMenuItem>(mContext, 0) {
                     @Override
                     public View getView(int position, View convertView, ViewGroup parent) {
                         return mOverflowPanelViewHelper.getView(
@@ -1232,14 +1083,11 @@
                     }
                 };
         overflowPanel.setAdapter(adapter);
-
         overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
-            MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
-            if (mOnMenuItemClickListener != null) {
-                mOnMenuItemClickListener.onMenuItemClick(menuItem);
-            }
+            ToolbarMenuItem menuItem =
+                    (ToolbarMenuItem) overflowPanel.getAdapter().getItem(position);
+            mCallbackWrapper.onMenuItemClicked(menuItem);
         });
-
         return overflowPanel;
     }
 
@@ -1252,7 +1100,7 @@
     }
 
     private Animation.AnimationListener createOverflowAnimationListener() {
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
+        return new Animation.AnimationListener() {
             @Override
             public void onAnimationStart(Animation animation) {
                 // Disable the overflow button while it's animating.
@@ -1270,7 +1118,6 @@
                 // actually ends.
                 mContentContainer.post(() -> {
                     setPanelsStatesAtRestingPosition();
-                    setContentAreaAsTouchableSurface();
                 });
             }
 
@@ -1278,12 +1125,11 @@
             public void onAnimationRepeat(Animation animation) {
             }
         };
-        return listener;
     }
 
     private static Size measure(View view) {
         Preconditions.checkState(view.getParent() == null);
-        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
     }
 
@@ -1372,13 +1218,11 @@
      * A helper for generating views for the overflow panel.
      */
     private static final class OverflowPanelViewHelper {
-
+        private final Context mContext;
         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;
@@ -1387,7 +1231,7 @@
             mCalculator = createMenuButton(null);
         }
 
-        public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
+        public View getView(ToolbarMenuItem menuItem, int minimumWidth, View convertView) {
             Objects.requireNonNull(menuItem);
             if (convertView != null) {
                 updateMenuItemButton(
@@ -1399,7 +1243,7 @@
             return convertView;
         }
 
-        public int calculateWidth(MenuItem menuItem) {
+        public int calculateWidth(ToolbarMenuItem menuItem) {
             updateMenuItemButton(
                     mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
             mCalculator.measure(
@@ -1407,18 +1251,18 @@
             return mCalculator.getMeasuredWidth();
         }
 
-        private View createMenuButton(MenuItem menuItem) {
+        private View createMenuButton(ToolbarMenuItem menuItem) {
             View button = createMenuItemButton(
                     mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
             button.setPadding(mSidePadding, 0, mSidePadding, 0);
             return button;
         }
 
-        private boolean shouldShowIcon(MenuItem menuItem) {
+        private boolean shouldShowIcon(ToolbarMenuItem menuItem) {
             if (menuItem != null) {
                 return menuItem.getGroupId() == android.R.id.textAssist;
             }
-            return false;
+            return  false;
         }
     }
 
@@ -1426,7 +1270,7 @@
      * Creates and returns a menu button for the specified menu item.
      */
     private static View createMenuItemButton(
-            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+            Context context, ToolbarMenuItem menuItem, int iconTextSpacing, boolean showIcon) {
         final View menuItemButton = LayoutInflater.from(context)
                 .inflate(R.layout.floating_popup_menu_button, null);
         if (menuItem != null) {
@@ -1438,8 +1282,8 @@
     /**
      * Updates the specified menu item button with the specified menu item data.
      */
-    private static void updateMenuItemButton(
-            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+    private static void updateMenuItemButton(View menuItemButton, ToolbarMenuItem menuItem,
+            int iconTextSpacing, boolean showIcon) {
         final TextView buttonText = menuItemButton.findViewById(
                 R.id.floating_toolbar_menu_item_text);
         buttonText.setEllipsize(null);
@@ -1453,15 +1297,12 @@
                 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);
-            }
+            buttonText.setPaddingRelative(0, 0, 0, 0);
         } else {
             buttonIcon.setVisibility(View.VISIBLE);
-            buttonIcon.setImageDrawable(menuItem.getIcon());
-            if (buttonText != null) {
-                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
-            }
+            buttonIcon.setImageDrawable(
+                    menuItem.getIcon().loadDrawable(menuItemButton.getContext()));
+            buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
         }
         final CharSequence contentDescription = menuItem.getContentDescription();
         if (TextUtils.isEmpty(contentDescription)) {
@@ -1476,36 +1317,21 @@
                 .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.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
         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) {
+    private static AnimatorSet createEnterAnimation(View view, Animator.AnimatorListener listener) {
         AnimatorSet animation = new AnimatorSet();
         animation.playTogether(
                 ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
+        animation.addListener(listener);
         return animation;
     }
 
@@ -1522,7 +1348,9 @@
         animation.playTogether(
                 ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
         animation.setStartDelay(startDelay);
-        animation.addListener(listener);
+        if (listener != null) {
+            animation.addListener(listener);
+        }
         return animation;
     }
 
@@ -1538,82 +1366,9 @@
         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;
+    private static void debugLog(String message) {
+        if (Log.isLoggable(FloatingToolbar.FLOATING_TOOLBAR_TAG, Log.DEBUG)) {
+            Log.v(TAG, message);
         }
     }
 }
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
index 6468183..a10b6a8 100644
--- a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
@@ -31,14 +31,6 @@
      */
     void onShown(WidgetInfo widgetInfo);
     /**
-     * The selection toolbar is hidden.
-     */
-    void onHidden(long widgetToken);
-    /**
-     * The selection toolbar is dismissed.
-     */
-    void onDismissed(long widgetToken);
-    /**
      * The selection toolbar has changed.
      */
     void onWidgetUpdated(WidgetInfo info);
@@ -47,6 +39,10 @@
      */
     void onMenuItemClicked(ToolbarMenuItem item);
     /**
+     * The toolbar doesn't be dismissed after showing on a given timeout.
+     */
+    void onToolbarShowTimeout();
+    /**
      * The error occurred when operating on the selection toolbar.
      */
     void onError(int errorCode);
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
index 6f66c9f..f33feae 100644
--- a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
@@ -28,6 +28,8 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
 import android.view.selectiontoolbar.ISelectionToolbarCallback;
 import android.view.selectiontoolbar.ShowInfo;
 import android.view.selectiontoolbar.ToolbarMenuItem;
@@ -42,6 +44,10 @@
 
     private static final String TAG = "SelectionToolbarRenderService";
 
+    // TODO(b/215497659): read from DeviceConfig
+    // The timeout to clean the cache if the client forgot to call dismiss()
+    private static final int CACHE_CLEAN_AFTER_SHOW_TIMEOUT_IN_MS = 10 * 60 * 1000; // 10 minutes
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      *
@@ -53,6 +59,10 @@
             "android.service.selectiontoolbar.SelectionToolbarRenderService";
 
     private Handler mHandler;
+    private ISelectionToolbarRenderServiceCallback mServiceCallback;
+
+    private final SparseArray<Pair<RemoteCallbackWrapper, CleanCacheRunnable>> mCache =
+            new SparseArray<>();
 
     /**
      * Binder to receive calls from system server.
@@ -61,10 +71,18 @@
             new ISelectionToolbarRenderService.Stub() {
 
         @Override
-        public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+        public void onShow(int callingUid, ShowInfo showInfo, ISelectionToolbarCallback callback) {
+            if (mCache.indexOfKey(callingUid) < 0) {
+                mCache.put(callingUid, new Pair<>(new RemoteCallbackWrapper(callback),
+                        new CleanCacheRunnable(callingUid)));
+            }
+            Pair<RemoteCallbackWrapper, CleanCacheRunnable> toolbarPair = mCache.get(callingUid);
+            CleanCacheRunnable cleanRunnable = toolbarPair.second;
+            mHandler.removeCallbacks(cleanRunnable);
             mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onShow,
-                    SelectionToolbarRenderService.this, showInfo,
-                    new RemoteCallbackWrapper(callback)));
+                    SelectionToolbarRenderService.this, callingUid, showInfo,
+                    toolbarPair.first));
+            mHandler.postDelayed(cleanRunnable, CACHE_CLEAN_AFTER_SHOW_TIMEOUT_IN_MS);
         }
 
         @Override
@@ -74,9 +92,20 @@
         }
 
         @Override
-        public void onDismiss(long widgetToken) {
+        public void onDismiss(int callingUid, long widgetToken) {
             mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onDismiss,
                     SelectionToolbarRenderService.this, widgetToken));
+            Pair<RemoteCallbackWrapper, CleanCacheRunnable> toolbarPair = mCache.get(callingUid);
+            if (toolbarPair != null) {
+                mHandler.removeCallbacks(toolbarPair.second);
+                mCache.remove(callingUid);
+            }
+        }
+
+        @Override
+        public void onConnected(IBinder callback) {
+            mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::handleOnConnected,
+                    SelectionToolbarRenderService.this, callback));
         }
     };
 
@@ -97,11 +126,28 @@
         return null;
     }
 
+    private void handleOnConnected(@NonNull IBinder callback) {
+        mServiceCallback = ISelectionToolbarRenderServiceCallback.Stub.asInterface(callback);
+    }
+
+    protected void transferTouch(@NonNull IBinder source, @NonNull IBinder target) {
+        final ISelectionToolbarRenderServiceCallback callback = mServiceCallback;
+        if (callback == null) {
+            Log.e(TAG, "transferTouch(): no server callback");
+            return;
+        }
+        try {
+            callback.transferTouch(source, target);
+        } catch (RemoteException e) {
+            // no-op
+        }
+    }
 
     /**
      * Called when showing the selection toolbar.
      */
-    public abstract void onShow(ShowInfo showInfo, RemoteCallbackWrapper callbackWrapper);
+    public abstract void onShow(int callingUid, ShowInfo showInfo,
+            RemoteCallbackWrapper callbackWrapper);
 
     /**
      * Called when hiding the selection toolbar.
@@ -115,13 +161,22 @@
     public abstract void onDismiss(long widgetToken);
 
     /**
-     * Add avadoc.
+     * Called when showing the selection toolbar for a specific timeout. This avoids the client
+     * forgot to call dismiss to clean the state.
+     */
+    public void onToolbarShowTimeout(int callingUid) {
+        // no-op
+    }
+
+    /**
+     * Callback to notify the client toolbar events.
      */
     public static final class RemoteCallbackWrapper implements SelectionToolbarRenderCallback {
 
         private final ISelectionToolbarCallback mRemoteCallback;
 
         RemoteCallbackWrapper(ISelectionToolbarCallback remoteCallback) {
+            // TODO(b/215497659): handle if the binder dies.
             mRemoteCallback = remoteCallback;
         }
 
@@ -130,25 +185,16 @@
             try {
                 mRemoteCallback.onShown(widgetInfo);
             } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
+                // no-op
             }
         }
 
         @Override
-        public void onHidden(long widgetToken) {
+        public void onToolbarShowTimeout() {
             try {
-                mRemoteCallback.onHidden(widgetToken);
+                mRemoteCallback.onToolbarShowTimeout();
             } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
-            }
-        }
-
-        @Override
-        public void onDismissed(long widgetToken) {
-            try {
-                mRemoteCallback.onDismissed(widgetToken);
-            } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
+                // no-op
             }
         }
 
@@ -157,7 +203,7 @@
             try {
                 mRemoteCallback.onWidgetUpdated(widgetInfo);
             } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
+                // no-op
             }
         }
 
@@ -166,7 +212,7 @@
             try {
                 mRemoteCallback.onMenuItemClicked(item);
             } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
+                // no-op
             }
         }
 
@@ -175,8 +221,37 @@
             try {
                 mRemoteCallback.onError(errorCode);
             } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
+                // no-op
             }
         }
     }
+
+    private class CleanCacheRunnable implements Runnable {
+
+        int mCleanUid;
+
+        CleanCacheRunnable(int cleanUid) {
+            mCleanUid = cleanUid;
+        }
+
+        @Override
+        public void run() {
+            Pair<RemoteCallbackWrapper, CleanCacheRunnable> toolbarPair = mCache.get(mCleanUid);
+            if (toolbarPair != null) {
+                Log.w(TAG, "CleanCacheRunnable: remove " + mCleanUid + " from cache.");
+                mCache.remove(mCleanUid);
+                onToolbarShowTimeout(mCleanUid);
+            }
+        }
+    }
+
+    /**
+     * A listener to notify the service to the transfer touch focus.
+     */
+    public interface TransferTouchListener {
+        /**
+         * Notify the service to transfer the touch focus.
+         */
+        void onTransferTouch(IBinder source, IBinder target);
+    }
 }
diff --git a/core/java/android/service/tracing/TraceReportService.java b/core/java/android/service/tracing/TraceReportService.java
new file mode 100644
index 0000000..3d16a3d
--- /dev/null
+++ b/core/java/android/service/tracing/TraceReportService.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.tracing;
+
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.tracing.TraceReportParams;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.UUID;
+
+/**
+ * Service to be sub-classed and exposed by (privileged) apps which want to report
+ * system traces.
+ * <p>
+ * Subclasses should implement the onReportTrace method to handle traces reported
+ * to them.
+ * </p>
+ * <pre>
+ *    public class SampleReportService extends TraceReportService {
+ *        public void onReportTrace(TraceParams args) {
+ *            // --- Implementation goes here ---
+ *        }
+ *    }
+ * </pre>
+ * <p>
+ * The service declaration in the application manifest must specify
+ * BIND_TRACE_REPORT_SERVICE in the permission attribute.
+ * </p>
+ * <pre>
+ *   &lt;application>
+ *        &lt;service android:name=".SampleReportService"
+ *               android:permission="android.permission.BIND_TRACE_REPORT_SERVICE">
+ *       &lt;/service>
+ *   &lt;/application>
+ * </pre>
+ *
+ * Moreover, the package containing this service must hold the DUMP and PACKAGE_USAGE_STATS
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi(client = PRIVILEGED_APPS)
+public class TraceReportService extends Service {
+    private static final String TAG = "TraceReportService";
+    private Messenger mMessenger = null;
+
+    /**
+     * Public to allow this to be used by TracingServiceProxy in system_server.
+     *
+     * @hide
+     */
+    public static final int MSG_REPORT_TRACE = 1;
+
+    /**
+     * Contains information about the trace which is being reported.
+     *
+     * @hide
+     */
+    @SystemApi(client = PRIVILEGED_APPS)
+    public static final class TraceParams {
+        private final ParcelFileDescriptor mFd;
+        private final UUID mUuid;
+
+        private TraceParams(TraceReportParams params) {
+            mFd = params.fd;
+            mUuid = new UUID(params.uuidMsb, params.uuidLsb);
+        }
+
+        /**
+         * Returns the ParcelFileDescriptor for the collected trace.
+         */
+        @NonNull
+        public ParcelFileDescriptor getFd() {
+            return mFd;
+        }
+
+        /**
+         * Returns the UUID of the trace; this is exactly the UUID created by the tracing system
+         * (i.e. Perfetto) and is also present inside the trace file.
+         */
+        @NonNull
+        public UUID getUuid() {
+            return mUuid;
+        }
+    }
+
+    // Methods to override.
+    /**
+     * Called when a trace is reported and sent to this class.
+     *
+     * Note: the trace file descriptor should not be persisted beyond the lifetime of this
+     * function as it is owned by the framework and will be closed immediately after this function
+     * returns: if future use of the fd is needed, it should be duped.
+     */
+    public void onReportTrace(@NonNull TraceParams args) {
+    }
+
+    // Optional methods to override.
+    // Realistically, these methods are internal implementation details but since this class is
+    // a SystemApi, it's better to err on the side of flexibility just in-case we need to override
+    // these methods down the line.
+
+    /**
+     * Handles binder calls from system_server.
+     */
+    public boolean onMessage(@NonNull Message msg) {
+        if (msg.what == MSG_REPORT_TRACE) {
+            if (!(msg.obj instanceof TraceReportParams)) {
+                Log.e(TAG, "Received invalid type for report trace message.");
+                return false;
+            }
+            TraceParams params = new TraceParams((TraceReportParams) msg.obj);
+            try {
+                onReportTrace(params);
+            } finally {
+                try {
+                    params.getFd().close();
+                } catch (IOException ignored) {
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns an IBinder for handling binder calls from system_server.
+     */
+    @Nullable
+    @Override
+    public IBinder onBind(@NonNull Intent intent) {
+        if (mMessenger == null) {
+            mMessenger = new Messenger(new Handler(Looper.getMainLooper(), this::onMessage));
+        }
+        return mMessenger.getBinder();
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
index 4520db3..8029b88 100644
--- a/core/java/android/tracing/ITracingServiceProxy.aidl
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -16,17 +16,25 @@
 
 package android.tracing;
 
+import android.tracing.TraceReportParams;
+
 /**
  * Binder interface for the TracingServiceProxy running in system_server.
  *
  * {@hide}
  */
-interface ITracingServiceProxy
-{
+interface ITracingServiceProxy {
     /**
      * Notifies system tracing app that a tracing session has ended. If a session is repurposed
      * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
      * there is no buffer available to dump.
      */
     oneway void notifyTraceSessionEnded(boolean sessionStolen);
+
+    /**
+     * Notifies the specified service that a trace has been captured. The contents of |params|
+     * contains the intended recipient (package and class) of this trace as well as a file
+     * descriptor to an unlinked trace |fd| (i.e. an fd opened using O_TMPFILE).
+     */
+    oneway void reportTrace(in TraceReportParams params);
 }
diff --git a/core/java/android/tracing/TraceReportParams.aidl b/core/java/android/tracing/TraceReportParams.aidl
new file mode 100644
index 0000000..f57386c
--- /dev/null
+++ b/core/java/android/tracing/TraceReportParams.aidl
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+import android.os.ParcelFileDescriptor;
+
+/*
+ * Parameters for a trace report.
+ *
+ * See ITracingServiceProxy::reportTrace for more details.
+ *
+ * @hide
+ */
+parcelable TraceReportParams {
+  // The package name containing the reporter service (see |reporterClassName|).
+  String reporterPackageName;
+
+  // The class name of the reporter service. The framework will bind to this service and pass the
+  // trace fd and metadata to this class.
+  // This class should be "trusted" (in practice this means being a priv_app + having DUMP and
+  // USAGE_STATS permissions).
+  String reporterClassName;
+
+  // The file descriptor for the trace file. This will be an unlinked file fd (i.e. created
+  // with O_TMPFILE); the intention is that reporter classes link this fd into a app-private
+  // folder for reporting when conditions are right (e.g. charging, on unmetered networks etc).
+  ParcelFileDescriptor fd;
+
+  // The least-significant-bytes of the UUID of this trace.
+  long uuidLsb;
+
+  // The most-significant-bytes of the UUID of this trace.
+  long uuidMsb;
+
+  // Flag indicating whether, instead of passing the fd from the trace collector, to pass a
+  // pipe fd from system_server and send the file over it.
+  //
+  // This flag is necessary because there is no good way to write a CTS test where a helper
+  // priv_app (in terms of SELinux) is needed (this is because priv_apps are supposed to be
+  // preinstalled on the system partition). By creating a pipe in system_server we work around
+  // this restriction. Note that there is a maximum allowed file size if this flag is set
+  // (see TracingServiceProxy). Further note that, even though SELinux may be worked around,
+  // manifest (i.e. framework) permissions are still checked even if this flag is set.
+  boolean usePipeForTesting;
+}
\ No newline at end of file
diff --git a/core/java/android/transparency/BinaryTransparencyManager.java b/core/java/android/transparency/BinaryTransparencyManager.java
new file mode 100644
index 0000000..18783f5
--- /dev/null
+++ b/core/java/android/transparency/BinaryTransparencyManager.java
@@ -0,0 +1,83 @@
+/*
+ * 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.transparency;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.os.IBinaryTransparencyService;
+
+import java.util.Map;
+
+/**
+ * BinaryTransparencyManager defines a number of system interfaces that other system apps or
+ * services can make use of, when trying to get more information about the various binaries
+ * that are installed on this device.
+ * @hide
+ */
+@SystemService(Context.BINARY_TRANSPARENCY_SERVICE)
+public class BinaryTransparencyManager {
+    private static final String TAG = "TransparencyManager";
+
+    private final Context mContext;
+    private final IBinaryTransparencyService mService;
+
+    /**
+     * Constructor
+     * @param context The calling context.
+     * @param service A valid instance of IBinaryTransparencyService.
+     * @hide
+     */
+    public BinaryTransparencyManager(Context context, IBinaryTransparencyService service) {
+        mContext = context;
+        mService = service;
+    }
+
+
+    /**
+     * Obtains a string containing information that describes the signed images that are installed
+     * on this device. Currently, this piece of information is identified as the VBMeta digest.
+     * @return A String containing the VBMeta Digest of the signed partitions loaded on this device.
+     */
+    @NonNull
+    public String getSignedImageInfo() {
+        try {
+            return mService.getSignedImageInfo();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a map of all installed APEXs consisting of package name to SHA256 hash of the
+     * package.
+     * @return A Map with the following entries: {apex package name : sha256 digest of package}
+     */
+    @NonNull
+    public Map getApexInfo() {
+        try {
+            Slog.d(TAG, "Calling backend's getApexInfo()");
+            return mService.getApexInfo();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+}
diff --git a/core/java/android/transparency/OWNERS b/core/java/android/transparency/OWNERS
new file mode 100644
index 0000000..75bf84c
--- /dev/null
+++ b/core/java/android/transparency/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+billylau@google.com
+vishwath@google.com
+mpgroover@google.com
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d6e074f..fa39380 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -115,14 +115,6 @@
     private int mCachedAppHeightCompat;
 
     /**
-     * Indicates that the application is started in a different rotation than the real display, so
-     * the display information may be adjusted. That ensures the methods {@link #getRotation},
-     * {@link #getRealSize}, {@link #getRealMetrics}, and {@link #getCutout} are consistent with how
-     * the application window is laid out.
-     */
-    private boolean mMayAdjustByFixedRotation;
-
-    /**
      * Cache if the application is the recents component.
      * TODO(b/179308296) Remove once Launcher addresses issue
      */
@@ -916,14 +908,15 @@
      * degrees counter-clockwise, to compensate rendering will be rotated by
      * 90 degrees clockwise and thus the returned value here will be
      * {@link Surface#ROTATION_90 Surface.ROTATION_90}.
+     *
+     * This rotation value will match the results of {@link #getMetrics}: this means that the
+     * rotation value will correspond to the activity if accessed through the activity.
      */
     @Surface.Rotation
     public int getRotation() {
         synchronized (mLock) {
             updateDisplayInfoLocked();
-            return mMayAdjustByFixedRotation
-                    ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
-                    : mDisplayInfo.rotation;
+            return getLocalRotation();
         }
     }
 
@@ -959,9 +952,15 @@
     public DisplayCutout getCutout() {
         synchronized (mLock) {
             updateDisplayInfoLocked();
-            return mMayAdjustByFixedRotation
-                    ? getDisplayAdjustments().getDisplayCutout(mDisplayInfo.displayCutout)
-                    : mDisplayInfo.displayCutout;
+            if (mResources == null) return mDisplayInfo.displayCutout;
+            final DisplayCutout localCutout = mDisplayInfo.displayCutout;
+            if (localCutout == null) return null;
+            int rotation = getLocalRotation();
+            if (rotation != mDisplayInfo.rotation) {
+                return localCutout.getRotated(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
+                        mDisplayInfo.rotation, rotation);
+            }
+            return localCutout;
         }
     }
 
@@ -977,15 +976,11 @@
     public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
         synchronized (mLock) {
             updateDisplayInfoLocked();
-            RoundedCorners roundedCorners;
-            if (mMayAdjustByFixedRotation) {
-                roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
-                        mDisplayInfo.roundedCorners,
-                        mDisplayInfo.rotation,
-                        mDisplayInfo.logicalWidth,
-                        mDisplayInfo.logicalHeight);
-            } else {
-                roundedCorners = mDisplayInfo.roundedCorners;
+            final RoundedCorners roundedCorners = mDisplayInfo.roundedCorners;
+            final @Surface.Rotation int rotation = getLocalRotation();
+            if (roundedCorners != null && rotation != mDisplayInfo.rotation) {
+                roundedCorners.rotate(rotation,
+                        mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             }
             return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
         }
@@ -1468,8 +1463,9 @@
             }
             outSize.x = mDisplayInfo.logicalWidth;
             outSize.y = mDisplayInfo.logicalHeight;
-            if (mMayAdjustByFixedRotation) {
-                getDisplayAdjustments().adjustSize(outSize, mDisplayInfo.rotation);
+            final @Surface.Rotation int rotation = getLocalRotation();
+            if (rotation != mDisplayInfo.rotation) {
+                adjustSize(outSize, mDisplayInfo.rotation, rotation);
             }
         }
     }
@@ -1537,8 +1533,9 @@
             }
             mDisplayInfo.getLogicalMetrics(outMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
-            if (mMayAdjustByFixedRotation) {
-                getDisplayAdjustments().adjustMetrics(outMetrics, mDisplayInfo.rotation);
+            final @Surface.Rotation int rotation = getLocalRotation();
+            if (rotation != mDisplayInfo.rotation) {
+                adjustMetrics(outMetrics, mDisplayInfo.rotation, rotation);
             }
         }
     }
@@ -1663,9 +1660,6 @@
                 }
             }
         }
-
-        mMayAdjustByFixedRotation = mResources != null
-                && mResources.hasOverrideDisplayAdjustments();
     }
 
     private void updateCachedAppSizeIfNeededLocked() {
@@ -1679,6 +1673,49 @@
         }
     }
 
+    /** Returns {@code false} if the width and height of display should swap. */
+    private static boolean noFlip(@Surface.Rotation int realRotation,
+            @Surface.Rotation int localRotation) {
+        // Check if the delta is rotated by 90 degrees.
+        return (realRotation - localRotation + 4) % 2 == 0;
+    }
+
+    /**
+     * Adjusts the given size by a rotation offset if necessary.
+     * @hide
+     */
+    private void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation,
+            @Surface.Rotation int localRotation) {
+        if (noFlip(realRotation, localRotation)) return;
+        final int w = size.x;
+        size.x = size.y;
+        size.y = w;
+    }
+
+    /**
+     * Adjusts the given metrics by a rotation offset if necessary.
+     * @hide
+     */
+    private void adjustMetrics(@NonNull DisplayMetrics metrics,
+            @Surface.Rotation int realRotation, @Surface.Rotation int localRotation) {
+        if (noFlip(realRotation, localRotation)) return;
+        int w = metrics.widthPixels;
+        metrics.widthPixels = metrics.heightPixels;
+        metrics.heightPixels = w;
+
+        w = metrics.noncompatWidthPixels;
+        metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
+        metrics.noncompatHeightPixels = w;
+    }
+
+    private @Surface.Rotation int getLocalRotation() {
+        if (mResources == null) return mDisplayInfo.rotation;
+        final @Surface.Rotation int localRotation =
+                mResources.getConfiguration().windowConfiguration.getDisplayRotation();
+        if (localRotation != WindowConfiguration.ROTATION_UNDEFINED) return localRotation;
+        return mDisplayInfo.rotation;
+    }
+
     // For debugging purposes
     @Override
     public String toString() {
@@ -1686,9 +1723,7 @@
             updateDisplayInfoLocked();
             final DisplayAdjustments adjustments = getDisplayAdjustments();
             mDisplayInfo.getAppMetrics(mTempMetrics, adjustments);
-            return "Display id " + mDisplayId + ": " + mDisplayInfo
-                    + (mMayAdjustByFixedRotation
-                            ? (", " + adjustments.getFixedRotationAdjustments() + ", ") : ", ")
+            return "Display id " + mDisplayId + ": " + mDisplayInfo + ", "
                     + mTempMetrics + ", isValid=" + mIsValid;
         }
     }
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index e307eff..bb50849 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -21,10 +21,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.DisplayMetrics;
 
 import java.util.Objects;
 
@@ -34,7 +30,6 @@
 
     private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
     private final Configuration mConfiguration = new Configuration(Configuration.EMPTY);
-    private FixedRotationAdjustments mFixedRotationAdjustments;
 
     @UnsupportedAppUsage
     public DisplayAdjustments() {
@@ -49,7 +44,6 @@
     public DisplayAdjustments(@NonNull DisplayAdjustments daj) {
         setCompatibilityInfo(daj.mCompatInfo);
         mConfiguration.setTo(daj.getConfiguration());
-        mFixedRotationAdjustments = daj.mFixedRotationAdjustments;
     }
 
     @UnsupportedAppUsage
@@ -90,97 +84,11 @@
         return mConfiguration;
     }
 
-    public void setFixedRotationAdjustments(FixedRotationAdjustments fixedRotationAdjustments) {
-        mFixedRotationAdjustments = fixedRotationAdjustments;
-    }
-
-    public FixedRotationAdjustments getFixedRotationAdjustments() {
-        return mFixedRotationAdjustments;
-    }
-
-    /** Returns {@code false} if the width and height of display should swap. */
-    private boolean noFlip(@Surface.Rotation int realRotation) {
-        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
-        if (rotationAdjustments == null) {
-            return true;
-        }
-        // Check if the delta is rotated by 90 degrees.
-        return (realRotation - rotationAdjustments.mRotation + 4) % 2 == 0;
-    }
-
-    /** Adjusts the given size if possible. */
-    public void adjustSize(@NonNull Point size, @Surface.Rotation int realRotation) {
-        if (noFlip(realRotation)) {
-            return;
-        }
-        final int w = size.x;
-        size.x = size.y;
-        size.y = w;
-    }
-
-    /** Adjusts the given metrics if possible. */
-    public void adjustMetrics(@NonNull DisplayMetrics metrics, @Surface.Rotation int realRotation) {
-        if (noFlip(realRotation)) {
-            return;
-        }
-        int w = metrics.widthPixels;
-        metrics.widthPixels = metrics.heightPixels;
-        metrics.heightPixels = w;
-
-        w = metrics.noncompatWidthPixels;
-        metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
-        metrics.noncompatHeightPixels = w;
-    }
-
-    /** Adjusts global display metrics that is available to applications. */
-    public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) {
-        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
-        if (rotationAdjustments == null) {
-            return;
-        }
-        metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth;
-        metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight;
-    }
-
-    /** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
-    @Nullable
-    public DisplayCutout getDisplayCutout(@Nullable DisplayCutout realCutout) {
-        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
-        return rotationAdjustments != null && rotationAdjustments.mRotatedDisplayCutout != null
-                ? rotationAdjustments.mRotatedDisplayCutout
-                : realCutout;
-    }
-
-    /**
-     * Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
-     * {@link RoundedCorners} is returned.
-     */
-    @Nullable
-    public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
-            @Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
-        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
-        if (realRoundedCorners == null || rotationAdjustments == null
-                || rotationAdjustments.mRotation == realRotation) {
-            return realRoundedCorners;
-        }
-
-        return realRoundedCorners.rotate(
-                rotationAdjustments.mRotation, displayWidth, displayHeight);
-    }
-
-    /** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
-    @Surface.Rotation
-    public int getRotation(@Surface.Rotation int realRotation) {
-        final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
-        return rotationAdjustments != null ? rotationAdjustments.mRotation : realRotation;
-    }
-
     @Override
     public int hashCode() {
         int hash = 17;
         hash = hash * 31 + Objects.hashCode(mCompatInfo);
         hash = hash * 31 + Objects.hashCode(mConfiguration);
-        hash = hash * 31 + Objects.hashCode(mFixedRotationAdjustments);
         return hash;
     }
 
@@ -191,101 +99,6 @@
         }
         DisplayAdjustments daj = (DisplayAdjustments)o;
         return Objects.equals(daj.mCompatInfo, mCompatInfo)
-                && Objects.equals(daj.mConfiguration, mConfiguration)
-                && Objects.equals(daj.mFixedRotationAdjustments, mFixedRotationAdjustments);
-    }
-
-    /**
-     * An application can be launched in different rotation than the real display. This class
-     * provides the information to adjust the values returned by {@link Display}.
-     * @hide
-     */
-    public static class FixedRotationAdjustments implements Parcelable {
-        /** The application-based rotation. */
-        @Surface.Rotation
-        final int mRotation;
-
-        /**
-         * The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according
-         * to rotation because it minus the region of screen decorations.
-         */
-        final int mAppWidth;
-
-        /** The rotated {@link DisplayInfo#appHeight}. */
-        final int mAppHeight;
-
-        /** Non-null if the device has cutout. */
-        @Nullable
-        final DisplayCutout mRotatedDisplayCutout;
-
-        public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight,
-                DisplayCutout cutout) {
-            mRotation = rotation;
-            mAppWidth = appWidth;
-            mAppHeight = appHeight;
-            mRotatedDisplayCutout = cutout;
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 17;
-            hash = hash * 31 + mRotation;
-            hash = hash * 31 + mAppWidth;
-            hash = hash * 31 + mAppHeight;
-            hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
-            return hash;
-        }
-
-        @Override
-        public boolean equals(@Nullable Object o) {
-            if (!(o instanceof FixedRotationAdjustments)) {
-                return false;
-            }
-            final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
-            return mRotation == other.mRotation
-                    && mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight
-                    && Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
-        }
-
-        @Override
-        public String toString() {
-            return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
-                    + " appWidth=" + mAppWidth + " appHeight=" + mAppHeight
-                    + " cutout=" + mRotatedDisplayCutout + "}";
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mRotation);
-            dest.writeInt(mAppWidth);
-            dest.writeInt(mAppHeight);
-            dest.writeTypedObject(
-                    new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
-        }
-
-        private FixedRotationAdjustments(Parcel in) {
-            mRotation = in.readInt();
-            mAppWidth = in.readInt();
-            mAppHeight = in.readInt();
-            final DisplayCutout.ParcelableWrapper cutoutWrapper =
-                    in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
-            mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;
-        }
-
-        public static final Creator<FixedRotationAdjustments> CREATOR =
-                new Creator<FixedRotationAdjustments>() {
-            public FixedRotationAdjustments createFromParcel(Parcel in) {
-                return new FixedRotationAdjustments(in);
-            }
-
-            public FixedRotationAdjustments[] newArray(int size) {
-                return new FixedRotationAdjustments[size];
-            }
-        };
+                && Objects.equals(daj.mConfiguration, mConfiguration);
     }
 }
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
index fc9661a..bf72a30 100644
--- a/core/java/android/view/ISurfaceControlViewHost.aidl
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -17,6 +17,8 @@
 package android.view;
 
 import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.InsetsState;
 
 /**
  * API from content embedder back to embedded content in SurfaceControlViewHost
@@ -25,4 +27,5 @@
 oneway interface ISurfaceControlViewHost {
     void onConfigurationChanged(in Configuration newConfig);
     void onDispatchDetachedFromWindow();
+    void onInsetsChanged(in InsetsState state, in Rect insetFrame);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index ccf1e44..32054b1 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -298,7 +298,7 @@
     */
     void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
             in IBinder hostInputToken, int flags, int privateFlags, int type,
-            out InputChannel outInputChannel);
+            in IBinder focusGrantToken, out InputChannel outInputChannel);
 
     /**
      * Update the flags on an input channel associated with a particular surface.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 85a9dbd..7e0d887 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -22,10 +22,13 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
 import android.view.accessibility.IAccessibilityEmbeddedConnection;
+import android.view.InsetsState;
 
 import java.util.Objects;
 
@@ -71,6 +74,16 @@
                 release();
             });
         }
+
+        @Override
+        public void onInsetsChanged(InsetsState state, Rect frame) {
+            if (mViewRoot != null) {
+                mViewRoot.mHandler.post(() -> {
+                    mViewRoot.setOverrideInsetsFrame(frame);
+                });
+            }
+            mWm.setInsetsState(state);
+        }
     }
 
     private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
@@ -168,6 +181,36 @@
             return mRemoteInterface;
         }
 
+        /**
+         * Forward a configuration to the remote SurfaceControlViewHost.
+         * This will cause View#onConfigurationChanged to be invoked on the remote
+         * end. This does not automatically cause the SurfaceControlViewHost
+         * to be resized. The root View of a SurfaceControlViewHost
+         * is more akin to a PopupWindow in that the size is user specified
+         * independent of configuration width and height.
+         *
+         * @param c The configuration to forward
+         */
+        public void notifyConfigurationChanged(@NonNull Configuration c) {
+            try {
+                getRemoteInterface().onConfigurationChanged(c);
+            } catch (RemoteException e) {
+                e.rethrowAsRuntimeException();
+            }
+        }
+
+        /**
+         * Tear down the remote SurfaceControlViewHost and cause
+         * View#onDetachedFromWindow to be invoked on the other side.
+         */
+        public void notifyDetachedFromWindow() {
+            try {
+                getRemoteInterface().onDispatchDetachedFromWindow();
+            } catch (RemoteException e) {
+                e.rethrowAsRuntimeException();
+            }
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -274,7 +317,7 @@
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
             return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
-                mViewRoot.getInputToken(), mRemoteInterface);
+                mWm.getFocusGrantToken(), mRemoteInterface);
         } else {
             return null;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7559273..4ff7e229 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8178,9 +8178,13 @@
                         // to User. Ideally View should handle the event when isVisibleToUser()
                         // becomes true where it should issue notifyViewEntered().
                         afm.notifyViewEntered(this);
+                    } else {
+                        afm.enableFillRequestActivityStarted();
                     }
                 } else if (!enter && !isFocused()) {
                     afm.notifyViewExited(this);
+                } else if (enter) {
+                    afm.enableFillRequestActivityStarted();
                 }
             }
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1496a4a..386b277 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -563,6 +563,8 @@
     @Nullable
     int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
     boolean mPerformContentCapture;
+    boolean mPerformAutoFill;
+
 
     boolean mReportNextDraw;
     /**
@@ -643,6 +645,7 @@
 
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
+    Rect mOverrideInsetsFrame;
 
     final Rect mPendingBackDropFrame = new Rect();
 
@@ -841,6 +844,7 @@
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
         mPerformContentCapture = true; // also true for the first time the view is added
+        mPerformAutoFill = true;
         mAdded = false;
         mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                 context);
@@ -4352,6 +4356,18 @@
         if (mPerformContentCapture) {
             performContentCaptureInitialReport();
         }
+
+        if (mPerformAutoFill) {
+            notifyEnterForAutoFillIfNeeded();
+        }
+    }
+
+    private void notifyEnterForAutoFillIfNeeded() {
+        mPerformAutoFill = false;
+        final AutofillManager afm = getAutofillManager();
+        if (afm != null) {
+            afm.notifyViewEnteredForActivityStarted(mView);
+        }
     }
 
     /**
@@ -8069,7 +8085,22 @@
 
     private void setFrame(Rect frame) {
         mWinFrame.set(frame);
-        mInsetsController.onFrameChanged(frame);
+        mInsetsController.onFrameChanged(mOverrideInsetsFrame != null ?
+            mOverrideInsetsFrame : frame);
+    }
+
+    /**
+     * In the normal course of operations we compute insets relative to
+     * the frame returned from relayout window. In the case of
+     * SurfaceControlViewHost, this frame is in local coordinates
+     * instead of global coordinates. We support this override
+     * frame so we can allow SurfaceControlViewHost to set a frame
+     * to be used to calculate insets, without disturbing the main
+     * mFrame.
+     */
+    void setOverrideInsetsFrame(Rect frame) {
+        mOverrideInsetsFrame = new Rect(frame);
+        mInsetsController.onFrameChanged(mOverrideInsetsFrame);
     }
 
     /**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index aa9ea19..039b50a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -607,6 +607,17 @@
          * @param hasCapture True if the window has pointer capture.
          */
         default public void onPointerCaptureChanged(boolean hasCapture) { };
+
+        /**
+         * Called from
+         * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}.
+         *
+         * @param appearance The newly applied appearance.
+         * @hide
+         */
+        default void onSystemBarAppearanceChanged(
+                @WindowInsetsController.Appearance int appearance) {
+        }
     }
 
     /** @hide */
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 02c8945..115e9e8 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -163,5 +163,10 @@
     public void onPointerCaptureChanged(boolean hasCapture) {
         mWrapped.onPointerCaptureChanged(hasCapture);
     }
+
+    @Override
+    public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
+        mWrapped.onSystemBarAppearanceChanged(appearance);
+    }
 }
 
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 3392edc..56f0915 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -21,11 +21,14 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.MergedConfiguration;
+import android.view.InsetsState;
+import android.view.IWindow;
 import android.window.ClientWindowFrames;
 import android.window.IOnBackInvokedCallback;
 
@@ -48,12 +51,14 @@
         int mDisplayId;
         IBinder mInputChannelToken;
         Region mInputRegion;
+        IWindow mClient;
         State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId,
-                IBinder inputChannelToken) {
+              IBinder inputChannelToken, IWindow client) {
             mSurfaceControl = sc;
             mParams.copyFrom(p);
             mDisplayId = displayId;
             mInputChannelToken = inputChannelToken;
+            mClient = client;
         }
     };
 
@@ -75,6 +80,8 @@
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
     private final IBinder mHostInputToken;
+    private final IBinder mFocusGrantToken = new Binder();
+    private InsetsState mInsetsState;
 
     private int mForceHeight = -1;
     private int mForceWidth = -1;
@@ -91,6 +98,10 @@
         mConfiguration.setTo(configuration);
     }
 
+    IBinder getFocusGrantToken() {
+        return mFocusGrantToken;
+    }
+
     /**
      * Utility API.
      */
@@ -153,10 +164,10 @@
                     mRealWm.grantInputChannel(displayId,
                         new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
                         window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type,
-                        outInputChannel);
+                        mFocusGrantToken, outInputChannel);
                 } else {
                     mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
-                        attrs.privateFlags, attrs.type, outInputChannel);
+                        attrs.privateFlags, attrs.type, mFocusGrantToken, outInputChannel);
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to grant input to surface: ", e);
@@ -164,7 +175,7 @@
         }
 
         final State state = new State(sc, attrs, displayId,
-                outInputChannel != null ? outInputChannel.getToken() : null);
+            outInputChannel != null ? outInputChannel.getToken() : null, window);
         synchronized (this) {
             mStateForWindow.put(window.asBinder(), state);
         }
@@ -312,6 +323,10 @@
             }
         }
 
+        if (mInsetsState != null) {
+            outInsetsState.set(mInsetsState);
+        }
+
         // Include whether the window is in touch mode.
         return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
     }
@@ -469,7 +484,7 @@
 
     @Override
     public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
-            IBinder hostInputToken, int flags, int privateFlags, int type,
+            IBinder hostInputToken, int flags, int privateFlags, int type, IBinder focusGrantToken,
             InputChannel outInputChannel) {
     }
 
@@ -501,4 +516,15 @@
     public boolean dropForAccessibility(IWindow window, int x, int y) {
         return false;
     }
+
+    public void setInsetsState(InsetsState state) {
+        mInsetsState = state;
+        for (State s : mStateForWindow.values()) {
+            try {
+                s.mClient.insetsChanged(state, false, false);
+            } catch (RemoteException e) {
+                // Too bad
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bb13c1e..60ccf67 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,7 @@
 
 package android.view.autofill;
 
+import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
@@ -538,6 +539,8 @@
      */
     public static final int NO_SESSION = Integer.MAX_VALUE;
 
+    private static final boolean HAS_FILL_DIALOG_UI_FEATURE = false;
+
     private final IAutoFillManager mService;
 
     private final Object mLock = new Object();
@@ -629,6 +632,29 @@
     @GuardedBy("mLock")
     @Nullable private Executor mRequestCallbackExecutor;
 
+    /**
+     * Indicates whether there are any fields that need to do a fill request
+     * after the activity starts.
+     *
+     * Note: This field will be set to true multiple times if there are many
+     * autofillable views. So needs to check mIsFillRequested at the same time to
+     * avoid re-trigger autofill.
+     */
+    private boolean mRequireAutofill;
+
+    /**
+     * Indicates whether there is already a field to do a fill request after
+     * the activity started.
+     *
+     * Autofill will automatically trigger a fill request after activity
+     * start if there is any field is autofillable. But if there is a field that
+     * triggered autofill, it is unnecessary to trigger again through
+     * AutofillManager#notifyViewEnteredForActivityStarted.
+     */
+    private boolean mIsFillRequested;
+
+    @Nullable private List<AutofillId> mFillDialogTriggerIds;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -766,6 +792,8 @@
         mContext = Objects.requireNonNull(context, "context cannot be null");
         mService = service;
         mOptions = context.getAutofillOptions();
+        mIsFillRequested = false;
+        mRequireAutofill = false;
 
         if (mOptions != null) {
             sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1042,6 +1070,39 @@
         notifyViewEntered(view, 0);
     }
 
+    /**
+     * The view is autofillable, marked to perform a fill request after layout if
+     * the field does not trigger a fill request.
+     *
+     * @hide
+     */
+    public void enableFillRequestActivityStarted() {
+        mRequireAutofill = true;
+    }
+
+    private boolean hasFillDialogUiFeature() {
+        return HAS_FILL_DIALOG_UI_FEATURE;
+    }
+
+    /**
+     * Notify autofill to do a fill request while the activity started.
+     *
+     * @hide
+     */
+    public void notifyViewEnteredForActivityStarted(@NonNull View view) {
+        if (!hasAutofillFeature() || !hasFillDialogUiFeature()) {
+            return;
+        }
+
+        if (!mRequireAutofill || mIsFillRequested) {
+            return;
+        }
+
+        int flags = FLAG_ACTIVITY_START;
+        flags |= FLAG_VIEW_NOT_FOCUSED;
+        notifyViewEntered(view, flags);
+    }
+
     @GuardedBy("mLock")
     private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
         if (isDisabledByServiceLocked()) {
@@ -1082,6 +1143,7 @@
         }
         AutofillCallback callback;
         synchronized (mLock) {
+            mIsFillRequested = true;
             callback = notifyViewEnteredLocked(view, flags);
         }
 
@@ -2026,6 +2088,8 @@
         mFillableIds = null;
         mSaveTriggerId = null;
         mIdShownFillUi = null;
+        mIsFillRequested = false;
+        mRequireAutofill = false;
         if (resetEnteredIds) {
             mEnteredIds = null;
         }
@@ -3031,6 +3095,29 @@
         client.autofillClientRunOnUiThread(runnable);
     }
 
+    private void setFillDialogTriggerIds(@Nullable List<AutofillId> ids) {
+        mFillDialogTriggerIds = ids;
+    }
+
+    /**
+     * Checks the id of autofill whether supported the fill dialog.
+     *
+     * @hide
+     */
+    public boolean isShowFillDialog(AutofillId id) {
+        if (!hasFillDialogUiFeature() || mFillDialogTriggerIds == null) {
+            return false;
+        }
+        final int size = mFillDialogTriggerIds.size();
+        for (int i = 0; i < size; i++) {
+            AutofillId fillId = mFillDialogTriggerIds.get(i);
+            if (fillId.equalsIgnoreSession(id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Implementation of the accessibility based compatibility.
      */
@@ -3736,6 +3823,13 @@
                         new FillCallback(callback, id));
             }
         }
+
+        public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.setFillDialogTriggerIds(ids));
+            }
+        }
     }
 
     private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 64507aa..2e5967c 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -148,4 +148,9 @@
      */
     void requestFillFromClient(int id, in InlineSuggestionsRequest request,
             in IFillCallback callback);
+
+    /**
+     * Notifies autofill ids that require to show the fill dialog.
+     */
+    void notifyFillDialogTriggerIds(in List<AutofillId> ids);
 }
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index b85fe7c..78509fe 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -972,6 +972,13 @@
      * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
      * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
      * used together with {@link #CURSOR_UPDATE_MONITOR}.
+     * <p>
+     * Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS} and
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} are included but specifying them can
+     * filter-out others.
+     * It can be CPU intensive to include all, filtering specific info is recommended.
+     * </p>
      */
     int CURSOR_UPDATE_IMMEDIATE = 1 << 0;
 
@@ -983,17 +990,69 @@
      * <p>
      * This flag can be used together with {@link #CURSOR_UPDATE_IMMEDIATE}.
      * </p>
+     * <p>
+     * Note by default all of {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS} and
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} are included but specifying them can
+     * filter-out others.
+     * It can be CPU intensive to include all, filtering specific info is recommended.
+     * </p>
      */
     int CURSOR_UPDATE_MONITOR = 1 << 1;
 
     /**
+     * The editor is requested to call
+     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
+     * with new {@link EditorBoundsInfo} whenever cursor/anchor position is changed. To disable
+     * monitoring, call {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
+     * <p>
+     * This flag can be used together with filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
+     * {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
+     * </p>
+     */
+    int CURSOR_UPDATE_FILTER_EDITOR_BOUNDS = 1 << 2;
+
+    /**
+     * The editor is requested to call
+     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
+     * with new character bounds {@link CursorAnchorInfo#getCharacterBounds(int)} whenever
+     * cursor/anchor position is changed. To disable
+     * monitoring, call {@link InputConnection#requestCursorUpdates(int)} again with this flag off.
+     * <p>
+     * This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER} and update flags
+     * {@link #CURSOR_UPDATE_IMMEDIATE} and {@link #CURSOR_UPDATE_MONITOR}.
+     * </p>
+     */
+    int CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS = 1 << 3;
+
+    /**
+     * The editor is requested to call
+     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
+     * with new Insertion marker info {@link CursorAnchorInfo#getInsertionMarkerFlags()},
+     * {@link CursorAnchorInfo#getInsertionMarkerBaseline()}, etc whenever cursor/anchor position is
+     * changed. To disable monitoring, call {@link InputConnection#requestCursorUpdates(int)} again
+     * with this flag off.
+     * <p>
+     * This flag can be combined with other filters: {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS} and update flags {@link #CURSOR_UPDATE_IMMEDIATE}
+     * and {@link #CURSOR_UPDATE_MONITOR}.
+     * </p>
+     */
+    int CURSOR_UPDATE_FILTER_INSERTION_MARKER = 1 << 4;
+
+    /**
      * Called by the input method to ask the editor for calling back
      * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} to
      * notify cursor/anchor locations.
      *
-     * @param cursorUpdateMode {@link #CURSOR_UPDATE_IMMEDIATE} and/or
-     * {@link #CURSOR_UPDATE_MONITOR}. Pass {@code 0} to disable the effect of
-     * {@link #CURSOR_UPDATE_MONITOR}.
+     * @param cursorUpdateMode any combination of update modes and filters:
+     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and date filters:
+     * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+     * Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
+     * rejected and method will return {@code false}.
      * @return {@code true} if the request is scheduled. {@code false} to indicate that when the
      *         application will not call {@link InputMethodManager#updateCursorAnchorInfo(
      *         android.view.View, CursorAnchorInfo)}.
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index a1d10f8..ff6903e8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -409,4 +409,12 @@
         // intentionally empty
     }
 
+    /**
+     * Initialize Ink window early-on.
+     * @hide
+     */
+    default void initInkWindow() {
+        // intentionally empty
+    }
+
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 849f3c9..f480b24 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,7 @@
 
 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_FILTER_EDITOR_BOUNDS;
 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;
@@ -1797,11 +1798,12 @@
             }
             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);
+                        CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR
+                                | CURSOR_UPDATE_FILTER_EDITOR_BOUNDS,
+                        mDisplayId);
             }
 
             try {
@@ -2449,6 +2451,17 @@
     }
 
     /**
+     * Get the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}.
+     *
+     * @hide
+     */
+    public int getUpdateCursorAnchorInfoMode() {
+        synchronized (mH) {
+            return mRequestUpdateCursorAnchorInfoMonitorMode;
+        }
+    }
+
+    /**
      * Report the current cursor location in its window.
      *
      * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead.
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
index 48af7b9..aaeb120 100644
--- a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
@@ -25,9 +25,8 @@
  */
 oneway interface ISelectionToolbarCallback {
     void onShown(in WidgetInfo info);
-    void onHidden(long widgetToken);
-    void onDismissed(long widgetToken);
     void onWidgetUpdated(in WidgetInfo info);
+    void onToolbarShowTimeout();
     void onMenuItemClicked(in ToolbarMenuItem item);
     void onError(int errorCode);
 }
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.java b/core/java/android/view/selectiontoolbar/SelectionContext.java
deleted file mode 100644
index 21b8d8f..0000000
--- a/core/java/android/view/selectiontoolbar/SelectionContext.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.selectiontoolbar;
-
-import android.annotation.NonNull;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
-/**
- * The class holds information for a selection.
- *
- * @hide
- */
-@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
-public final class SelectionContext implements Parcelable {
-
-    /**
-     * The start index of a selection.
-     */
-    private final int mStartIndex;
-
-    /**
-     * The end index of a selection.
-     */
-    private final int mEndIndex;
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    /* package-private */ SelectionContext(
-            int startIndex,
-            int endIndex) {
-        this.mStartIndex = startIndex;
-        this.mEndIndex = endIndex;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * The start index of a selection.
-     */
-    @DataClass.Generated.Member
-    public int getStartIndex() {
-        return mStartIndex;
-    }
-
-    /**
-     * The end index of a selection.
-     */
-    @DataClass.Generated.Member
-    public int getEndIndex() {
-        return mEndIndex;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "SelectionContext { " +
-                "startIndex = " + mStartIndex + ", " +
-                "endIndex = " + mEndIndex +
-        " }";
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public boolean equals(@android.annotation.Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(SelectionContext other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        SelectionContext that = (SelectionContext) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && mStartIndex == that.mStartIndex
-                && mEndIndex == that.mEndIndex;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + mStartIndex;
-        _hash = 31 * _hash + mEndIndex;
-        return _hash;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeInt(mStartIndex);
-        dest.writeInt(mEndIndex);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ SelectionContext(@NonNull android.os.Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        int startIndex = in.readInt();
-        int endIndex = in.readInt();
-
-        this.mStartIndex = startIndex;
-        this.mEndIndex = endIndex;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<SelectionContext> CREATOR
-            = new Parcelable.Creator<SelectionContext>() {
-        @Override
-        public SelectionContext[] newArray(int size) {
-            return new SelectionContext[size];
-        }
-
-        @Override
-        public SelectionContext createFromParcel(@NonNull android.os.Parcel in) {
-            return new SelectionContext(in);
-        }
-    };
-
-    /**
-     * A builder for {@link SelectionContext}
-     */
-    @SuppressWarnings("WeakerAccess")
-    @DataClass.Generated.Member
-    public static final class Builder {
-
-        private int mStartIndex;
-        private int mEndIndex;
-
-        private long mBuilderFieldsSet = 0L;
-
-        /**
-         * Creates a new Builder.
-         *
-         * @param startIndex
-         *   The start index of a selection.
-         * @param endIndex
-         *   The end index of a selection.
-         */
-        public Builder(
-                int startIndex,
-                int endIndex) {
-            mStartIndex = startIndex;
-            mEndIndex = endIndex;
-        }
-
-        /**
-         * The start index of a selection.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setStartIndex(int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x1;
-            mStartIndex = value;
-            return this;
-        }
-
-        /**
-         * The end index of a selection.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setEndIndex(int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mEndIndex = value;
-            return this;
-        }
-
-        /** Builds the instance. This builder should not be touched after calling this! */
-        public @NonNull SelectionContext build() {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
-
-            SelectionContext o = new SelectionContext(
-                    mStartIndex,
-                    mEndIndex);
-            return o;
-        }
-
-        private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
-                throw new IllegalStateException(
-                        "This Builder should not be reused. Use a new Builder instance instead");
-            }
-        }
-    }
-
-    @DataClass.Generated(
-            time = 1639488292248L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java",
-            inputSignatures = "private final  int mStartIndex\nprivate final  int mEndIndex\nclass SelectionContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
index ba45b85..6de0316 100644
--- a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -47,6 +47,16 @@
     private static final String REMOTE_SELECTION_TOOLBAR_ENABLED =
             "remote_selection_toolbar_enabled";
 
+    /**
+     * Used to mark a toolbar that has no toolbar token id.
+     */
+    public static final long NO_TOOLBAR_ID = 0;
+
+    /**
+     * The error code that do not allow to create multiple toolbar.
+     */
+    public static final int ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR = 1;
+
     @NonNull
     private final Context mContext;
     private final ISelectionToolbarManager mService;
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.java b/core/java/android/view/selectiontoolbar/ShowInfo.java
index bbbd5c0..594b6bc 100644
--- a/core/java/android/view/selectiontoolbar/ShowInfo.java
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.java
@@ -17,10 +17,14 @@
 package android.view.selectiontoolbar;
 
 import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.IBinder;
 import android.os.Parcelable;
 
 import com.android.internal.util.DataClass;
 
+import java.util.List;
+
 
 /**
  * The class holds menu information for render service to render the selection toolbar.
@@ -29,14 +33,47 @@
  */
 @DataClass(genToString = true, genEqualsHashCode = true)
 public final class ShowInfo implements Parcelable {
+
     /**
      * The token that is used to identify the selection toolbar. This is initially set to 0
      * until a selection toolbar has been created for the showToolbar request.
      */
     private final long mWidgetToken;
 
-    // TODO: add members when the code really uses it
+    /**
+     * If the toolbar menu items need to be re-layout.
+     */
+    private final boolean mLayoutRequired;
 
+    /**
+     * The menu items to be rendered in the selection toolbar.
+     */
+    @NonNull
+    private final List<ToolbarMenuItem> mMenuItems;
+
+    /**
+     * A rect specifying where the selection toolbar on the screen.
+     */
+    @NonNull
+    private final Rect mContentRect;
+
+    /**
+     * A recommended maximum suggested width of the selection toolbar.
+     */
+    private final int mSuggestedWidth;
+
+    /**
+     * The portion of the screen that is available to the selection toolbar.
+     */
+    @NonNull
+    private final Rect mViewPortOnScreen;
+
+    /**
+     * The host application's input token, this allows the remote render service to transfer
+     * the touch focus to the host application.
+     */
+    @NonNull
+    private final IBinder mHostInputToken;
 
 
 
@@ -57,24 +94,108 @@
      * Creates a new ShowInfo.
      *
      * @param widgetToken
-     *   The token that is used to identify the selection toolbar.
+     *   The token that is used to identify the selection toolbar. This is initially set to 0
+     *   until a selection toolbar has been created for the showToolbar request.
+     * @param layoutRequired
+     *   If the toolbar menu items need to be re-layout.
+     * @param menuItems
+     *   The menu items to be rendered in the selection toolbar.
+     * @param contentRect
+     *   A rect specifying where the selection toolbar on the screen.
+     * @param suggestedWidth
+     *   A recommended maximum suggested width of the selection toolbar.
+     * @param viewPortOnScreen
+     *   The portion of the screen that is available to the selection toolbar.
+     * @param hostInputToken
+     *   The host application's input token, this allows the remote render service to transfer
+     *   the touch focus to the host application.
      */
     @DataClass.Generated.Member
     public ShowInfo(
-            long widgetToken) {
+            long widgetToken,
+            boolean layoutRequired,
+            @NonNull List<ToolbarMenuItem> menuItems,
+            @NonNull Rect contentRect,
+            int suggestedWidth,
+            @NonNull Rect viewPortOnScreen,
+            @NonNull IBinder hostInputToken) {
         this.mWidgetToken = widgetToken;
+        this.mLayoutRequired = layoutRequired;
+        this.mMenuItems = menuItems;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMenuItems);
+        this.mContentRect = contentRect;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mContentRect);
+        this.mSuggestedWidth = suggestedWidth;
+        this.mViewPortOnScreen = viewPortOnScreen;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mViewPortOnScreen);
+        this.mHostInputToken = hostInputToken;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostInputToken);
 
         // onConstructed(); // You can define this method to get a callback
     }
 
     /**
-     * The token that is used to identify the selection toolbar.
+     * The token that is used to identify the selection toolbar. This is initially set to 0
+     * until a selection toolbar has been created for the showToolbar request.
      */
     @DataClass.Generated.Member
     public long getWidgetToken() {
         return mWidgetToken;
     }
 
+    /**
+     * If the toolbar menu items need to be re-layout.
+     */
+    @DataClass.Generated.Member
+    public boolean isLayoutRequired() {
+        return mLayoutRequired;
+    }
+
+    /**
+     * The menu items to be rendered in the selection toolbar.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<ToolbarMenuItem> getMenuItems() {
+        return mMenuItems;
+    }
+
+    /**
+     * A rect specifying where the selection toolbar on the screen.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Rect getContentRect() {
+        return mContentRect;
+    }
+
+    /**
+     * A recommended maximum suggested width of the selection toolbar.
+     */
+    @DataClass.Generated.Member
+    public int getSuggestedWidth() {
+        return mSuggestedWidth;
+    }
+
+    /**
+     * The portion of the screen that is available to the selection toolbar.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Rect getViewPortOnScreen() {
+        return mViewPortOnScreen;
+    }
+
+    /**
+     * The host application's input token, this allows the remote render service to transfer
+     * the touch focus to the host application.
+     */
+    @DataClass.Generated.Member
+    public @NonNull IBinder getHostInputToken() {
+        return mHostInputToken;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -82,7 +203,13 @@
         // String fieldNameToString() { ... }
 
         return "ShowInfo { " +
-                "widgetToken = " + mWidgetToken +
+                "widgetToken = " + mWidgetToken + ", " +
+                "layoutRequired = " + mLayoutRequired + ", " +
+                "menuItems = " + mMenuItems + ", " +
+                "contentRect = " + mContentRect + ", " +
+                "suggestedWidth = " + mSuggestedWidth + ", " +
+                "viewPortOnScreen = " + mViewPortOnScreen + ", " +
+                "hostInputToken = " + mHostInputToken +
         " }";
     }
 
@@ -99,7 +226,13 @@
         ShowInfo that = (ShowInfo) o;
         //noinspection PointlessBooleanExpression
         return true
-                && mWidgetToken == that.mWidgetToken;
+                && mWidgetToken == that.mWidgetToken
+                && mLayoutRequired == that.mLayoutRequired
+                && java.util.Objects.equals(mMenuItems, that.mMenuItems)
+                && java.util.Objects.equals(mContentRect, that.mContentRect)
+                && mSuggestedWidth == that.mSuggestedWidth
+                && java.util.Objects.equals(mViewPortOnScreen, that.mViewPortOnScreen)
+                && java.util.Objects.equals(mHostInputToken, that.mHostInputToken);
     }
 
     @Override
@@ -110,6 +243,12 @@
 
         int _hash = 1;
         _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+        _hash = 31 * _hash + Boolean.hashCode(mLayoutRequired);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mMenuItems);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mContentRect);
+        _hash = 31 * _hash + mSuggestedWidth;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mViewPortOnScreen);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         return _hash;
     }
 
@@ -119,7 +258,15 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (mLayoutRequired) flg |= 0x2;
+        dest.writeByte(flg);
         dest.writeLong(mWidgetToken);
+        dest.writeParcelableList(mMenuItems, flags);
+        dest.writeTypedObject(mContentRect, flags);
+        dest.writeInt(mSuggestedWidth);
+        dest.writeTypedObject(mViewPortOnScreen, flags);
+        dest.writeStrongBinder(mHostInputToken);
     }
 
     @Override
@@ -133,9 +280,31 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
+        boolean layoutRequired = (flg & 0x2) != 0;
         long widgetToken = in.readLong();
+        List<ToolbarMenuItem> menuItems = new java.util.ArrayList<>();
+        in.readParcelableList(menuItems, ToolbarMenuItem.class.getClassLoader());
+        Rect contentRect = (Rect) in.readTypedObject(Rect.CREATOR);
+        int suggestedWidth = in.readInt();
+        Rect viewPortOnScreen = (Rect) in.readTypedObject(Rect.CREATOR);
+        IBinder hostInputToken = (IBinder) in.readStrongBinder();
 
         this.mWidgetToken = widgetToken;
+        this.mLayoutRequired = layoutRequired;
+        this.mMenuItems = menuItems;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMenuItems);
+        this.mContentRect = contentRect;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mContentRect);
+        this.mSuggestedWidth = suggestedWidth;
+        this.mViewPortOnScreen = viewPortOnScreen;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mViewPortOnScreen);
+        this.mHostInputToken = hostInputToken;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHostInputToken);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -155,10 +324,10 @@
     };
 
     @DataClass.Generated(
-            time = 1639488262761L,
+            time = 1643186262604L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java",
-            inputSignatures = "private final  long mWidgetToken\nclass ShowInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  long mWidgetToken\nprivate final  boolean mLayoutRequired\nprivate final @android.annotation.NonNull java.util.List<android.view.selectiontoolbar.ToolbarMenuItem> mMenuItems\nprivate final @android.annotation.NonNull android.graphics.Rect mContentRect\nprivate final  int mSuggestedWidth\nprivate final @android.annotation.NonNull android.graphics.Rect mViewPortOnScreen\nprivate final @android.annotation.NonNull android.os.IBinder mHostInputToken\nclass ShowInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
index 5af232c..89347c6 100644
--- a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
 import android.os.Parcelable;
+import android.view.MenuItem;
 
 import com.android.internal.util.DataClass;
 
@@ -31,10 +33,84 @@
 public final class ToolbarMenuItem implements Parcelable {
 
     /**
+     * The priority of menu item is unknown.
+     */
+    public static final int PRIORITY_UNKNOWN = 0;
+
+    /**
+     * The priority of menu item is shown in primary selection toolbar.
+     */
+    public static final int PRIORITY_PRIMARY = 1;
+
+    /**
+     * The priority of menu item is shown in overflow selection toolbar.
+     */
+    public static final int PRIORITY_OVERFLOW = 2;
+
+    /**
      * The id of the menu item.
+     *
+     * @see MenuItem#getItemId()
      */
     private final int mItemId;
 
+    /**
+     * The title of the menu item.
+     *
+     * @see MenuItem#getTitle()
+     */
+    @NonNull
+    private final CharSequence mTitle;
+
+    /**
+     * The content description of the menu item.
+     *
+     * @see MenuItem#getContentDescription()
+     */
+    @Nullable
+    private final CharSequence mContentDescription;
+
+    /**
+     * The group id of the menu item.
+     *
+     * @see MenuItem#getGroupId()
+     */
+    private final int mGroupId;
+
+    /**
+     * The icon id of the menu item.
+     *
+     * @see MenuItem#getIcon()
+     */
+    @Nullable
+    private final Icon mIcon;
+
+    /**
+     * The tooltip text of the menu item.
+     *
+     * @see MenuItem#getTooltipText()
+     */
+    @Nullable
+    private final CharSequence mTooltipText;
+
+    /**
+     * The priority of the menu item used to display the order of the menu item.
+     */
+    private final int mPriority;
+
+    /**
+     * Returns the priority from a given {@link MenuItem}.
+     */
+    public static int getPriorityFromMenuItem(MenuItem menuItem) {
+        if (menuItem.requiresActionButton()) {
+            return PRIORITY_PRIMARY;
+        } else if (menuItem.requiresOverflow()) {
+            return PRIORITY_OVERFLOW;
+        }
+        return PRIORITY_UNKNOWN;
+    }
+
+
 
 
     // Code below generated by codegen v1.0.23.
@@ -50,22 +126,118 @@
     //@formatter:off
 
 
+    @android.annotation.IntDef(prefix = "PRIORITY_", value = {
+        PRIORITY_UNKNOWN,
+        PRIORITY_PRIMARY,
+        PRIORITY_OVERFLOW
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Priority {}
+
+    @DataClass.Generated.Member
+    public static String priorityToString(@Priority int value) {
+        switch (value) {
+            case PRIORITY_UNKNOWN:
+                    return "PRIORITY_UNKNOWN";
+            case PRIORITY_PRIMARY:
+                    return "PRIORITY_PRIMARY";
+            case PRIORITY_OVERFLOW:
+                    return "PRIORITY_OVERFLOW";
+            default: return Integer.toHexString(value);
+        }
+    }
+
     @DataClass.Generated.Member
     /* package-private */ ToolbarMenuItem(
-            int itemId) {
+            int itemId,
+            @NonNull CharSequence title,
+            @Nullable CharSequence contentDescription,
+            int groupId,
+            @Nullable Icon icon,
+            @Nullable CharSequence tooltipText,
+            int priority) {
         this.mItemId = itemId;
+        this.mTitle = title;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mTitle);
+        this.mContentDescription = contentDescription;
+        this.mGroupId = groupId;
+        this.mIcon = icon;
+        this.mTooltipText = tooltipText;
+        this.mPriority = priority;
 
         // onConstructed(); // You can define this method to get a callback
     }
 
     /**
      * The id of the menu item.
+     *
+     * @see MenuItem#getItemId()
      */
     @DataClass.Generated.Member
     public int getItemId() {
         return mItemId;
     }
 
+    /**
+     * The title of the menu item.
+     *
+     * @see MenuItem#getTitle()
+     */
+    @DataClass.Generated.Member
+    public @NonNull CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * The content description of the menu item.
+     *
+     * @see MenuItem#getContentDescription()
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * The group id of the menu item.
+     *
+     * @see MenuItem#getGroupId()
+     */
+    @DataClass.Generated.Member
+    public int getGroupId() {
+        return mGroupId;
+    }
+
+    /**
+     * The icon id of the menu item.
+     *
+     * @see MenuItem#getIcon()
+     */
+    @DataClass.Generated.Member
+    public @Nullable Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * The tooltip text of the menu item.
+     *
+     * @see MenuItem#getTooltipText()
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getTooltipText() {
+        return mTooltipText;
+    }
+
+    /**
+     * The priority of the menu item used to display the order of the menu item.
+     */
+    @DataClass.Generated.Member
+    public int getPriority() {
+        return mPriority;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -73,7 +245,13 @@
         // String fieldNameToString() { ... }
 
         return "ToolbarMenuItem { " +
-                "itemId = " + mItemId +
+                "itemId = " + mItemId + ", " +
+                "title = " + mTitle + ", " +
+                "contentDescription = " + mContentDescription + ", " +
+                "groupId = " + mGroupId + ", " +
+                "icon = " + mIcon + ", " +
+                "tooltipText = " + mTooltipText + ", " +
+                "priority = " + mPriority +
         " }";
     }
 
@@ -90,7 +268,13 @@
         ToolbarMenuItem that = (ToolbarMenuItem) o;
         //noinspection PointlessBooleanExpression
         return true
-                && mItemId == that.mItemId;
+                && mItemId == that.mItemId
+                && java.util.Objects.equals(mTitle, that.mTitle)
+                && java.util.Objects.equals(mContentDescription, that.mContentDescription)
+                && mGroupId == that.mGroupId
+                && java.util.Objects.equals(mIcon, that.mIcon)
+                && java.util.Objects.equals(mTooltipText, that.mTooltipText)
+                && mPriority == that.mPriority;
     }
 
     @Override
@@ -101,6 +285,12 @@
 
         int _hash = 1;
         _hash = 31 * _hash + mItemId;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTitle);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mContentDescription);
+        _hash = 31 * _hash + mGroupId;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mIcon);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTooltipText);
+        _hash = 31 * _hash + mPriority;
         return _hash;
     }
 
@@ -110,7 +300,18 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (mContentDescription != null) flg |= 0x4;
+        if (mIcon != null) flg |= 0x10;
+        if (mTooltipText != null) flg |= 0x20;
+        dest.writeByte(flg);
         dest.writeInt(mItemId);
+        dest.writeCharSequence(mTitle);
+        if (mContentDescription != null) dest.writeCharSequence(mContentDescription);
+        dest.writeInt(mGroupId);
+        if (mIcon != null) dest.writeTypedObject(mIcon, flags);
+        if (mTooltipText != null) dest.writeCharSequence(mTooltipText);
+        dest.writeInt(mPriority);
     }
 
     @Override
@@ -124,9 +325,24 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
         int itemId = in.readInt();
+        CharSequence title = (CharSequence) in.readCharSequence();
+        CharSequence contentDescription = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence();
+        int groupId = in.readInt();
+        Icon icon = (flg & 0x10) == 0 ? null : (Icon) in.readTypedObject(Icon.CREATOR);
+        CharSequence tooltipText = (flg & 0x20) == 0 ? null : (CharSequence) in.readCharSequence();
+        int priority = in.readInt();
 
         this.mItemId = itemId;
+        this.mTitle = title;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mTitle);
+        this.mContentDescription = contentDescription;
+        this.mGroupId = groupId;
+        this.mIcon = icon;
+        this.mTooltipText = tooltipText;
+        this.mPriority = priority;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -153,6 +369,12 @@
     public static final class Builder {
 
         private int mItemId;
+        private @NonNull CharSequence mTitle;
+        private @Nullable CharSequence mContentDescription;
+        private int mGroupId;
+        private @Nullable Icon mIcon;
+        private @Nullable CharSequence mTooltipText;
+        private int mPriority;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -161,14 +383,42 @@
          *
          * @param itemId
          *   The id of the menu item.
+         * @param title
+         *   The title of the menu item.
+         * @param contentDescription
+         *   The content description of the menu item.
+         * @param groupId
+         *   The group id of the menu item.
+         * @param icon
+         *   The icon id of the menu item.
+         * @param tooltipText
+         *   The tooltip text of the menu item.
+         * @param priority
+         *   The priority of the menu item used to display the order of the menu item.
          */
         public Builder(
-                int itemId) {
+                int itemId,
+                @NonNull CharSequence title,
+                @Nullable CharSequence contentDescription,
+                int groupId,
+                @Nullable Icon icon,
+                @Nullable CharSequence tooltipText,
+                int priority) {
             mItemId = itemId;
+            mTitle = title;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mTitle);
+            mContentDescription = contentDescription;
+            mGroupId = groupId;
+            mIcon = icon;
+            mTooltipText = tooltipText;
+            mPriority = priority;
         }
 
         /**
          * The id of the menu item.
+         *
+         * @see MenuItem#getItemId()
          */
         @DataClass.Generated.Member
         public @NonNull Builder setItemId(int value) {
@@ -178,18 +428,100 @@
             return this;
         }
 
+        /**
+         * The title of the menu item.
+         *
+         * @see MenuItem#getTitle()
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTitle(@NonNull CharSequence value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mTitle = value;
+            return this;
+        }
+
+        /**
+         * The content description of the menu item.
+         *
+         * @see MenuItem#getContentDescription()
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setContentDescription(@NonNull CharSequence value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mContentDescription = value;
+            return this;
+        }
+
+        /**
+         * The group id of the menu item.
+         *
+         * @see MenuItem#getGroupId()
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setGroupId(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mGroupId = value;
+            return this;
+        }
+
+        /**
+         * The icon id of the menu item.
+         *
+         * @see MenuItem#getIcon()
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setIcon(@NonNull Icon value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mIcon = value;
+            return this;
+        }
+
+        /**
+         * The tooltip text of the menu item.
+         *
+         * @see MenuItem#getTooltipText()
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTooltipText(@NonNull CharSequence value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mTooltipText = value;
+            return this;
+        }
+
+        /**
+         * The priority of the menu item used to display the order of the menu item.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setPriority(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mPriority = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull ToolbarMenuItem build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x2; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             ToolbarMenuItem o = new ToolbarMenuItem(
-                    mItemId);
+                    mItemId,
+                    mTitle,
+                    mContentDescription,
+                    mGroupId,
+                    mIcon,
+                    mTooltipText,
+                    mPriority);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x2) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -197,10 +529,10 @@
     }
 
     @DataClass.Generated(
-            time = 1639488328542L,
+            time = 1643200806234L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java",
-            inputSignatures = "private final  int mItemId\nclass ToolbarMenuItem extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "public static final  int PRIORITY_UNKNOWN\npublic static final  int PRIORITY_PRIMARY\npublic static final  int PRIORITY_OVERFLOW\nprivate final  int mItemId\nprivate final @android.annotation.NonNull java.lang.CharSequence mTitle\nprivate final @android.annotation.Nullable java.lang.CharSequence mContentDescription\nprivate final  int mGroupId\nprivate final @android.annotation.Nullable android.graphics.drawable.Icon mIcon\nprivate final @android.annotation.Nullable java.lang.CharSequence mTooltipText\nprivate final  int mPriority\npublic static  int getPriorityFromMenuItem(android.view.MenuItem)\nclass ToolbarMenuItem extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.java b/core/java/android/view/selectiontoolbar/WidgetInfo.java
index 961d8ac..5d0fd47 100644
--- a/core/java/android/view/selectiontoolbar/WidgetInfo.java
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.java
@@ -17,7 +17,10 @@
 package android.view.selectiontoolbar;
 
 import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.SurfaceControlViewHost;
 
 import com.android.internal.util.DataClass;
 
@@ -35,7 +38,18 @@
      */
     private final long mWidgetToken;
 
-    // TODO: add members when the code really uses it
+    /**
+     * A Rect that defines the size and positioning of the remote view with respect to
+     * its host window.
+     */
+    @NonNull
+    private final Rect mContentRect;
+
+    /**
+     * The SurfacePackage pointing to the remote view.
+     */
+    @NonNull
+    private final SurfaceControlViewHost.SurfacePackage mSurfacePackage;
 
 
 
@@ -57,11 +71,24 @@
      *
      * @param widgetToken
      *   The token that is used to identify the selection toolbar.
+     * @param contentRect
+     *   A Rect that defines the size and positioning of the remote view with respect to
+     *   its host window.
+     * @param surfacePackage
+     *   The SurfacePackage pointing to the remote view.
      */
     @DataClass.Generated.Member
     public WidgetInfo(
-            long widgetToken) {
+            long widgetToken,
+            @NonNull Rect contentRect,
+            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
         this.mWidgetToken = widgetToken;
+        this.mContentRect = contentRect;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mContentRect);
+        this.mSurfacePackage = surfacePackage;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSurfacePackage);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -74,6 +101,23 @@
         return mWidgetToken;
     }
 
+    /**
+     * A Rect that defines the size and positioning of the remote view with respect to
+     * its host window.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Rect getContentRect() {
+        return mContentRect;
+    }
+
+    /**
+     * The SurfacePackage pointing to the remote view.
+     */
+    @DataClass.Generated.Member
+    public @NonNull SurfaceControlViewHost.SurfacePackage getSurfacePackage() {
+        return mSurfacePackage;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -81,7 +125,9 @@
         // String fieldNameToString() { ... }
 
         return "WidgetInfo { " +
-                "widgetToken = " + mWidgetToken +
+                "widgetToken = " + mWidgetToken + ", " +
+                "contentRect = " + mContentRect + ", " +
+                "surfacePackage = " + mSurfacePackage +
         " }";
     }
 
@@ -98,7 +144,9 @@
         WidgetInfo that = (WidgetInfo) o;
         //noinspection PointlessBooleanExpression
         return true
-                && mWidgetToken == that.mWidgetToken;
+                && mWidgetToken == that.mWidgetToken
+                && java.util.Objects.equals(mContentRect, that.mContentRect)
+                && java.util.Objects.equals(mSurfacePackage, that.mSurfacePackage);
     }
 
     @Override
@@ -109,16 +157,20 @@
 
         int _hash = 1;
         _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mContentRect);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mSurfacePackage);
         return _hash;
     }
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         dest.writeLong(mWidgetToken);
+        dest.writeTypedObject(mContentRect, flags);
+        dest.writeTypedObject(mSurfacePackage, flags);
     }
 
     @Override
@@ -128,13 +180,21 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ WidgetInfo(@NonNull android.os.Parcel in) {
+    /* package-private */ WidgetInfo(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         long widgetToken = in.readLong();
+        Rect contentRect = (Rect) in.readTypedObject(Rect.CREATOR);
+        SurfaceControlViewHost.SurfacePackage surfacePackage = (SurfaceControlViewHost.SurfacePackage) in.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR);
 
         this.mWidgetToken = widgetToken;
+        this.mContentRect = contentRect;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mContentRect);
+        this.mSurfacePackage = surfacePackage;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSurfacePackage);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -148,16 +208,16 @@
         }
 
         @Override
-        public WidgetInfo createFromParcel(@NonNull android.os.Parcel in) {
+        public WidgetInfo createFromParcel(@NonNull Parcel in) {
             return new WidgetInfo(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1639488254020L,
+            time = 1643281495056L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java",
-            inputSignatures = "private final  long mWidgetToken\nclass WidgetInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  long mWidgetToken\nprivate final @android.annotation.NonNull android.graphics.Rect mContentRect\nprivate final @android.annotation.NonNull android.view.SurfaceControlViewHost.SurfacePackage mSurfacePackage\nclass WidgetInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f14c251..280e7df 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -141,7 +141,7 @@
      * and WebView to attempt to darken web content by algorithmic darkening when
      * appropriate.
      *
-     * Refer to {@link #setAllowAlgorithmicDarkening} for detail.
+     * Refer to {@link #setAlgorithmicDarkeningAllowed} for detail.
      *
      * @hide
      */
@@ -1558,7 +1558,7 @@
      * {@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}.
+     * {@link #setAlgorithmicDarkeningAllowed}.
      */
     public void setForceDark(@ForceDark int forceDark) {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1604,7 +1604,7 @@
      *
      * @param allow allow algorithmic darkening or not.
      */
-    public void setAllowAlgorithmicDarkening(boolean allow) {
+    public void setAlgorithmicDarkeningAllowed(boolean allow) {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
     }
 
@@ -1613,9 +1613,9 @@
      * The default is false.
      *
      * @return if the algorithmic darkening is allowed or not.
-     * @see #setAllowAlgorithmicDarkening
+     * @see #setAlgorithmicDarkeningAllowed
      */
-    public boolean getAllowAlgorithmicDarkening() {
+    public boolean isAlgorithmicDarkeningAllowed() {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
         return false;
     }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index dd70d69..3a7a544 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4567,6 +4567,19 @@
             if (layout == null) {
                 return;
             }
+            int mode = imm.getUpdateCursorAnchorInfoMode();
+            boolean includeEditorBounds =
+                    (mode & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0;
+            boolean includeCharacterBounds =
+                    (mode & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0;
+            boolean includeInsertionMarker =
+                    (mode & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0;
+            boolean includeAll =
+                    (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker);
+
+            includeEditorBounds |= includeAll;
+            includeCharacterBounds |= includeAll;
+            includeInsertionMarker |= includeAll;
 
             final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder;
             builder.reset();
@@ -4579,68 +4592,79 @@
             mTextView.getLocationOnScreen(mTmpIntOffset);
             mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
             builder.setMatrix(mViewToScreenMatrix);
-            final RectF bounds = new RectF();
-            mTextView.getBoundsOnScreen(bounds, false /* clipToParent */);
-            EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
-            //TODO(b/210039666): add Handwriting bounds once they're available.
-            builder.setEditorBoundsInfo(
-                    boundsBuilder.setEditorBounds(bounds).build());
 
-            final float viewportToContentHorizontalOffset =
-                    mTextView.viewportToContentHorizontalOffset();
-            final float viewportToContentVerticalOffset =
-                    mTextView.viewportToContentVerticalOffset();
-
-            final CharSequence text = mTextView.getText();
-            if (text instanceof Spannable) {
-                final Spannable sp = (Spannable) text;
-                int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
-                int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
-                if (composingTextEnd < composingTextStart) {
-                    final int temp = composingTextEnd;
-                    composingTextEnd = composingTextStart;
-                    composingTextStart = temp;
-                }
-                final boolean hasComposingText =
-                        (0 <= composingTextStart) && (composingTextStart < composingTextEnd);
-                if (hasComposingText) {
-                    final CharSequence composingText = text.subSequence(composingTextStart,
-                            composingTextEnd);
-                    builder.setComposingText(composingTextStart, composingText);
-                    mTextView.populateCharacterBounds(builder, composingTextStart,
-                            composingTextEnd, viewportToContentHorizontalOffset,
-                            viewportToContentVerticalOffset);
-                }
+            if (includeEditorBounds) {
+                final RectF bounds = new RectF();
+                mTextView.getBoundsOnScreen(bounds, false /* clipToParent */);
+                EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
+                //TODO(b/210039666): add Handwriting bounds once they're available.
+                builder.setEditorBoundsInfo(
+                        boundsBuilder.setEditorBounds(bounds).build());
             }
 
-            // Treat selectionStart as the insertion point.
-            if (0 <= selectionStart) {
-                final int offset = selectionStart;
-                final int line = layout.getLineForOffset(offset);
-                final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
-                        + viewportToContentHorizontalOffset;
-                final float insertionMarkerTop = layout.getLineTop(line)
-                        + viewportToContentVerticalOffset;
-                final float insertionMarkerBaseline = layout.getLineBaseline(line)
-                        + viewportToContentVerticalOffset;
-                final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
-                        + viewportToContentVerticalOffset;
-                final boolean isTopVisible = mTextView
-                        .isPositionVisible(insertionMarkerX, insertionMarkerTop);
-                final boolean isBottomVisible = mTextView
-                        .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
-                int insertionMarkerFlags = 0;
-                if (isTopVisible || isBottomVisible) {
-                    insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+            if (includeCharacterBounds || includeInsertionMarker) {
+                final float viewportToContentHorizontalOffset =
+                        mTextView.viewportToContentHorizontalOffset();
+                final float viewportToContentVerticalOffset =
+                        mTextView.viewportToContentVerticalOffset();
+
+                if (includeCharacterBounds) {
+                    final CharSequence text = mTextView.getText();
+                    if (text instanceof Spannable) {
+                        final Spannable sp = (Spannable) text;
+                        int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
+                        int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
+                        if (composingTextEnd < composingTextStart) {
+                            final int temp = composingTextEnd;
+                            composingTextEnd = composingTextStart;
+                            composingTextStart = temp;
+                        }
+                        final boolean hasComposingText =
+                                (0 <= composingTextStart) && (composingTextStart
+                                        < composingTextEnd);
+                        if (hasComposingText) {
+                            final CharSequence composingText = text.subSequence(composingTextStart,
+                                    composingTextEnd);
+                            builder.setComposingText(composingTextStart, composingText);
+                            mTextView.populateCharacterBounds(builder, composingTextStart,
+                                    composingTextEnd, viewportToContentHorizontalOffset,
+                                    viewportToContentVerticalOffset);
+                        }
+                    }
                 }
-                if (!isTopVisible || !isBottomVisible) {
-                    insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+
+                if (includeInsertionMarker) {
+                    // Treat selectionStart as the insertion point.
+                    if (0 <= selectionStart) {
+                        final int offset = selectionStart;
+                        final int line = layout.getLineForOffset(offset);
+                        final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
+                                + viewportToContentHorizontalOffset;
+                        final float insertionMarkerTop = layout.getLineTop(line)
+                                + viewportToContentVerticalOffset;
+                        final float insertionMarkerBaseline = layout.getLineBaseline(line)
+                                + viewportToContentVerticalOffset;
+                        final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
+                                + viewportToContentVerticalOffset;
+                        final boolean isTopVisible = mTextView
+                                .isPositionVisible(insertionMarkerX, insertionMarkerTop);
+                        final boolean isBottomVisible = mTextView
+                                .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
+                        int insertionMarkerFlags = 0;
+                        if (isTopVisible || isBottomVisible) {
+                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+                        }
+                        if (!isTopVisible || !isBottomVisible) {
+                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+                        }
+                        if (layout.isRtlCharAt(offset)) {
+                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+                        }
+                        builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
+                                insertionMarkerBaseline, insertionMarkerBottom,
+                                insertionMarkerFlags);
+                    }
                 }
-                if (layout.isRtlCharAt(offset)) {
-                    insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
-                }
-                builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
-                        insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
             }
 
             imm.updateCursorAnchorInfo(mTextView, builder.build());
diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java
index 9a07975..3a3eb74 100644
--- a/core/java/android/window/ConfigurationHelper.java
+++ b/core/java/android/window/ConfigurationHelper.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ResourcesManager;
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -99,6 +100,10 @@
         if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
             return true;
         }
+        // If the display rotation has changed, we also need to update resources.
+        if (isDisplayRotationChanged(config, newConfig)) {
+            return true;
+        }
         return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
     }
 
@@ -129,4 +134,15 @@
 
         return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
     }
+
+    private static boolean isDisplayRotationChanged(@NonNull Configuration config,
+            @NonNull Configuration newConfig) {
+        final int origRot = config.windowConfiguration.getDisplayRotation();
+        final int newRot = newConfig.windowConfiguration.getDisplayRotation();
+        if (newRot == WindowConfiguration.ROTATION_UNDEFINED
+                || origRot == WindowConfiguration.ROTATION_UNDEFINED) {
+            return false;
+        }
+        return origRot != newRot;
+    }
 }
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index fd1e848..3fa62e0 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -599,6 +599,7 @@
         private final Rect mTransitionBounds = new Rect();
         private HardwareBuffer mThumbnail;
         private int mAnimations;
+        private @ColorInt int mBackgroundColor;
 
         private AnimationOptions(int type) {
             mType = type;
@@ -608,6 +609,7 @@
             mType = in.readInt();
             mEnterResId = in.readInt();
             mExitResId = in.readInt();
+            mBackgroundColor = in.readInt();
             mOverrideTaskTransition = in.readBoolean();
             mPackageName = in.readString();
             mTransitionBounds.readFromParcel(in);
@@ -624,11 +626,12 @@
         }
 
         public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
-                int exitResId, boolean overrideTaskTransition) {
+                int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) {
             AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
             options.mPackageName = packageName;
             options.mEnterResId = enterResId;
             options.mExitResId = exitResId;
+            options.mBackgroundColor = backgroundColor;
             options.mOverrideTaskTransition = overrideTaskTransition;
             return options;
         }
@@ -673,6 +676,10 @@
             return mExitResId;
         }
 
+        public @ColorInt int getBackgroundColor() {
+            return mBackgroundColor;
+        }
+
         public boolean getOverrideTaskTransition() {
             return mOverrideTaskTransition;
         }
@@ -698,6 +705,7 @@
             dest.writeInt(mType);
             dest.writeInt(mEnterResId);
             dest.writeInt(mExitResId);
+            dest.writeInt(mBackgroundColor);
             dest.writeBoolean(mOverrideTaskTransition);
             dest.writeString(mPackageName);
             mTransitionBounds.writeToParcel(dest, flags);
@@ -740,7 +748,7 @@
 
         @Override
         public String toString() {
-            return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+            return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName
                     + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
         }
     }
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
index fbdbbfb..65526eb 100644
--- a/core/java/com/android/internal/app/BlockedAppActivity.java
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -35,6 +36,9 @@
     private static final String TAG = "BlockedAppActivity";
     private static final String PACKAGE_NAME = "com.android.internal.app";
     private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+    private static final String EXTRA_BLOCKED_ACTIVITY_INFO =
+            PACKAGE_NAME + ".extra.BLOCKED_ACTIVITY_INFO";
+    private static final String EXTRA_STREAMED_DEVICE = PACKAGE_NAME + ".extra.STREAMED_DEVICE";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -48,17 +52,30 @@
             return;
         }
 
+        CharSequence appLabel = null;
         String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
-        if (TextUtils.isEmpty(packageName)) {
-            Slog.wtf(TAG, "Invalid package: " + packageName);
+        ActivityInfo activityInfo = intent.getParcelableExtra(EXTRA_BLOCKED_ACTIVITY_INFO);
+        if (activityInfo != null) {
+            appLabel = activityInfo.loadLabel(getPackageManager());
+        } else if (!TextUtils.isEmpty(packageName)) {
+            appLabel = getAppLabel(userId, packageName);
+        }
+
+        if (TextUtils.isEmpty(appLabel)) {
+            Slog.wtf(TAG, "Invalid package: " + packageName + " or activity info: " + activityInfo);
             finish();
             return;
         }
 
-        CharSequence appLabel = getAppLabel(userId, packageName);
-
-        mAlertParams.mTitle = getString(R.string.app_blocked_title);
-        mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+        CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE);
+        if (!TextUtils.isEmpty(streamedDeviceName)) {
+            mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel);
+            mAlertParams.mMessage =
+                    getString(R.string.app_streaming_blocked_message, streamedDeviceName);
+        } else {
+            mAlertParams.mTitle = getString(R.string.app_blocked_title);
+            mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+        }
         mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
         setupAlert();
     }
@@ -83,4 +100,19 @@
                 .putExtra(Intent.EXTRA_USER_ID, userId)
                 .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
     }
+
+    /**
+     * Creates an intent that launches {@link BlockedAppActivity} when app streaming is blocked.
+     *
+     * Using this method and providing a non-empty {@code streamedDeviceName} will cause the dialog
+     * to use streaming-specific error messages.
+     */
+    public static Intent createStreamingBlockedIntent(int userId, ActivityInfo activityInfo,
+            CharSequence streamedDeviceName) {
+        return new Intent()
+                .setClassName("android", BlockedAppActivity.class.getName())
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(EXTRA_BLOCKED_ACTIVITY_INFO, activityInfo)
+                .putExtra(EXTRA_STREAMED_DEVICE, streamedDeviceName);
+    }
 }
diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
index ce2d229..33209e1 100644
--- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
+++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -27,6 +29,7 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.TextView;
+
 import com.android.internal.R;
 
 /**
@@ -48,6 +51,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         final Intent intent = getIntent();
         mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
         mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 29bb311..630c271 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -211,7 +211,10 @@
         // It is possible that any other bit is used as a valid flag in a future release.
         // We should reject the entire request in such a case.
         final int knownFlagMask = InputConnection.CURSOR_UPDATE_IMMEDIATE
-                | InputConnection.CURSOR_UPDATE_MONITOR;
+                | InputConnection.CURSOR_UPDATE_MONITOR
+                | InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS
+                | InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER
+                | InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS;
         final int unknownFlags = cursorUpdateMode & ~knownFlagMask;
         if (unknownFlags != 0) {
             if (DEBUG) {
diff --git a/core/java/com/android/internal/os/IBinaryTransparencyService.aidl b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
new file mode 100644
index 0000000..9be686a
--- /dev/null
+++ b/core/java/com/android/internal/os/IBinaryTransparencyService.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.os;
+
+/**
+ * "Backend" interface used by {@link android.os.BinaryTransparencyManager} to talk to the
+ * BinaryTransparencyService that actually implements the measurement and information aggregation
+ * functionality.
+ *
+ * @see BinaryTransparencyManager
+ */
+interface IBinaryTransparencyService {
+    String getSignedImageInfo();
+
+    Map getApexInfo();
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index fd1d86b..0b9773e 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -2,6 +2,7 @@
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file *Cpu* = file:CPU_OWNERS
 per-file *Binder* = file:BINDER_OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index d7eeb7b..a50282e 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1034,6 +1034,12 @@
     @Override
     public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
         updateColorViews(null /* insets */, true /* animate */);
+        if (mWindow != null) {
+            final Window.Callback callback = mWindow.getCallback();
+            if (callback != null) {
+                callback.onSystemBarAppearanceChanged(appearance);
+            }
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index fbf0f83..da24832 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -63,4 +63,6 @@
 
     void startStylusHandwriting(int requestId, in InputChannel channel,
             in List<MotionEvent> events);
+
+    void initInkWindow();
 }
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
index f47700c..f7af67b 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/FloatingToolbarPopup.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget.floatingtoolbar;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.MenuItem;
@@ -84,7 +85,8 @@
      * @see PopupWindow#setFocusable(boolean)
      * @see PopupWindow.OnDismissListener
      */
-    boolean setOutsideTouchable(boolean outsideTouchable, PopupWindow.OnDismissListener onDismiss);
+    boolean setOutsideTouchable(boolean outsideTouchable,
+            @Nullable PopupWindow.OnDismissListener onDismiss);
 
     /**
      * Returns {@link RemoteFloatingToolbarPopup} implementation if the system selection toolbar
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
index b3a8128..8c2eb10 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java
@@ -16,13 +16,47 @@
 
 package com.android.internal.widget.floatingtoolbar;
 
+import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UiThread;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
 import android.view.MenuItem;
+import android.view.SurfaceView;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
 import android.view.selectiontoolbar.SelectionToolbarManager;
+import android.view.selectiontoolbar.ShowInfo;
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 
@@ -34,55 +68,495 @@
  */
 public final class RemoteFloatingToolbarPopup implements FloatingToolbarPopup {
 
-    private final SelectionToolbarManager mSelectionToolbarManager;
-    // Parent for the popup window.
-    private final View mParent;
+    private static final boolean DEBUG =
+            Log.isLoggable(FloatingToolbar.FLOATING_TOOLBAR_TAG, Log.VERBOSE);
 
-    public RemoteFloatingToolbarPopup(Context context, View parent) {
-        // TODO: implement it
-        mParent = Objects.requireNonNull(parent);
-        mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
+    private static final int TOOLBAR_STATE_SHOWN = 1;
+    private static final int TOOLBAR_STATE_HIDDEN = 2;
+    private static final int TOOLBAR_STATE_DISMISSED = 3;
+
+    @IntDef(prefix = {"TOOLBAR_STATE_"}, value = {
+            TOOLBAR_STATE_SHOWN,
+            TOOLBAR_STATE_HIDDEN,
+            TOOLBAR_STATE_DISMISSED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ToolbarState {
     }
 
+    @NonNull
+    private final SelectionToolbarManager mSelectionToolbarManager;
+    // Parent for the popup window.
+    @NonNull
+    private final View mParent;
+    // A popup window used for showing menu items rendered by the remote system process
+    @NonNull
+    private final PopupWindow mPopupWindow;
+    // The callback to handle remote rendered selection toolbar.
+    @NonNull
+    private final SelectionToolbarCallbackImpl mSelectionToolbarCallback;
+
+    // tracks this popup state.
+    private @ToolbarState int mState;
+
+    // The token of the current showing floating toolbar.
+    private long mFloatingToolbarToken;
+    private final Rect mPreviousContentRect = new Rect();
+    private List<MenuItem> mMenuItems;
+    private MenuItem.OnMenuItemClickListener mMenuItemClickListener;
+    private int mSuggestedWidth;
+    private final Rect mScreenViewPort = new Rect();
+    private boolean mWidthChanged = true;
+
+    private final int[] mCoordsOnScreen = new int[2];
+    private final int[] mCoordsOnWindow = new int[2];
+
+    public RemoteFloatingToolbarPopup(Context context, View parent) {
+        mParent = Objects.requireNonNull(parent);
+        mPopupWindow = createPopupWindow(context);
+        mSelectionToolbarManager = context.getSystemService(SelectionToolbarManager.class);
+        mSelectionToolbarCallback = new SelectionToolbarCallbackImpl(this);
+        mFloatingToolbarToken = NO_TOOLBAR_ID;
+    }
+
+    @UiThread
     @Override
     public void show(List<MenuItem> menuItems,
             MenuItem.OnMenuItemClickListener menuItemClickListener, Rect contentRect) {
-        // TODO: implement it
+        Objects.requireNonNull(menuItems);
+        Objects.requireNonNull(menuItemClickListener);
+        if (isShowing() && Objects.equals(menuItems, mMenuItems)
+                && Objects.equals(contentRect, mPreviousContentRect)) {
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "Ignore duplicate show() for the same content.");
+            }
+            return;
+        }
+
+        boolean isLayoutRequired = mMenuItems == null
+                || !MenuItemRepr.reprEquals(menuItems, mMenuItems)
+                || mWidthChanged;
+        if (isLayoutRequired) {
+            mSelectionToolbarManager.dismissToolbar(mFloatingToolbarToken);
+            doDismissPopupWindow();
+        }
+        mMenuItemClickListener = menuItemClickListener;
+        mMenuItems = menuItems;
+
+        mParent.getWindowVisibleDisplayFrame(mScreenViewPort);
+        final int suggestWidth = mSuggestedWidth > 0
+                ? mSuggestedWidth
+                : mParent.getResources().getDimensionPixelSize(
+                        R.dimen.floating_toolbar_preferred_width);
+        final ShowInfo showInfo = new ShowInfo(
+                mFloatingToolbarToken, isLayoutRequired,
+                getToolbarMenuItems(mMenuItems),
+                contentRect,
+                suggestWidth,
+                mScreenViewPort,
+                mParent.getViewRootImpl().getInputToken());
+        if (DEBUG) {
+            Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                    "RemoteFloatingToolbarPopup.show() for " + showInfo);
+        }
+        mSelectionToolbarManager.showToolbar(showInfo, mSelectionToolbarCallback);
+        mPreviousContentRect.set(contentRect);
     }
 
+    @UiThread
+    @Override
+    public void dismiss() {
+        if (mState == TOOLBAR_STATE_DISMISSED) {
+            Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                    "The floating toolbar already dismissed.");
+            return;
+        }
+        if (DEBUG) {
+            Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                    "RemoteFloatingToolbarPopup.dismiss().");
+        }
+        mSelectionToolbarManager.dismissToolbar(mFloatingToolbarToken);
+        doDismissPopupWindow();
+    }
+
+    @UiThread
     @Override
     public void hide() {
-        // TODO: implement it
+        if (mState == TOOLBAR_STATE_DISMISSED || mState == TOOLBAR_STATE_HIDDEN) {
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "The floating toolbar already dismissed or hidden.");
+            }
+            return;
+        }
+        if (DEBUG) {
+            Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                    "RemoteFloatingToolbarPopup.hide().");
+        }
+        mSelectionToolbarManager.hideToolbar(mFloatingToolbarToken);
+        mState = TOOLBAR_STATE_HIDDEN;
+        mPopupWindow.dismiss();
     }
 
+    @UiThread
     @Override
     public void setSuggestedWidth(int suggestedWidth) {
-        // TODO: implement it
+        int difference = Math.abs(suggestedWidth - mSuggestedWidth);
+        mWidthChanged = difference > (mSuggestedWidth * 0.2);
+        mSuggestedWidth = suggestedWidth;
     }
 
     @Override
     public void setWidthChanged(boolean widthChanged) {
-        // no-op
+        mWidthChanged = widthChanged;
     }
 
-    @Override
-    public void dismiss() {
-        // TODO: implement it
-    }
-
+    @UiThread
     @Override
     public boolean isHidden() {
-        return false;
+        return mState == TOOLBAR_STATE_HIDDEN;
     }
 
+    @UiThread
     @Override
     public boolean isShowing() {
-        return false;
+        return mState == TOOLBAR_STATE_SHOWN;
     }
 
+    @UiThread
     @Override
     public boolean setOutsideTouchable(boolean outsideTouchable,
-            PopupWindow.OnDismissListener onDismiss) {
-        return false;
+            @Nullable PopupWindow.OnDismissListener onDismiss) {
+        if (mState == TOOLBAR_STATE_DISMISSED) {
+            return false;
+        }
+        boolean ret = false;
+        if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
+            mPopupWindow.setOutsideTouchable(outsideTouchable);
+            mPopupWindow.setFocusable(!outsideTouchable);
+            mPopupWindow.update();
+            ret = true;
+        }
+        mPopupWindow.setOnDismissListener(onDismiss);
+        return ret;
+    }
+
+    private void updatePopupWindowContent(WidgetInfo widgetInfo) {
+        if (DEBUG) {
+            Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG, "updatePopupWindowContent.");
+        }
+        ViewGroup contentContainer = (ViewGroup) mPopupWindow.getContentView();
+        contentContainer.removeAllViews();
+        SurfaceView surfaceView = new SurfaceView(mParent.getContext());
+        surfaceView.setZOrderOnTop(true);
+        surfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
+        surfaceView.setChildSurfacePackage(widgetInfo.getSurfacePackage());
+        contentContainer.addView(surfaceView);
+    }
+
+    private MenuItem getMenuItemByToolbarMenuItem(ToolbarMenuItem toolbarMenuItem) {
+        for (MenuItem item : mMenuItems) {
+            if (toolbarMenuItem.getItemId() == item.getItemId()) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    private Point getCoordinatesInWindow(int x, int y) {
+        // 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(mCoordsOnScreen);
+        mParent.getRootView().getLocationInWindow(mCoordsOnWindow);
+        int windowLeftOnScreen = mCoordsOnScreen[0] - mCoordsOnWindow[0];
+        int windowTopOnScreen = mCoordsOnScreen[1] - mCoordsOnWindow[1];
+        return new Point(Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+    }
+
+    private static List<ToolbarMenuItem> getToolbarMenuItems(List<MenuItem> menuItems) {
+        final List<ToolbarMenuItem> list = new ArrayList<>(menuItems.size());
+        for (MenuItem menuItem : menuItems) {
+            // TODO: use ToolbarMenuItem.Builder(MenuItem) instead
+            ToolbarMenuItem toolbarMenuItem = new ToolbarMenuItem.Builder(menuItem.getItemId(),
+                    menuItem.getTitle(), menuItem.getContentDescription(), menuItem.getGroupId(),
+                    convertDrawableToIcon(menuItem.getIcon()),
+                    menuItem.getTooltipText(),
+                    ToolbarMenuItem.getPriorityFromMenuItem(menuItem)).build();
+            list.add(toolbarMenuItem);
+        }
+        return list;
+    }
+
+    private static Icon convertDrawableToIcon(Drawable drawable) {
+        if (drawable == null) {
+            return null;
+        }
+        if (drawable instanceof BitmapDrawable) {
+            final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+            if (bitmapDrawable.getBitmap() != null) {
+                return Icon.createWithBitmap(bitmapDrawable.getBitmap());
+            }
+        }
+        final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(),  canvas.getHeight());
+        drawable.draw(canvas);
+        return Icon.createWithBitmap(bitmap);
+    }
+
+    private static PopupWindow createPopupWindow(Context content) {
+        ViewGroup popupContentHolder = new LinearLayout(content);
+        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+        popupWindow.setClippingEnabled(false);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+        popupWindow.setAnimationStyle(0);
+        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        return popupWindow;
+    }
+
+    private void doDismissPopupWindow() {
+        if (DEBUG) {
+            Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG, "RemoteFloatingToolbarPopup.doDismiss().");
+        }
+        mState = TOOLBAR_STATE_DISMISSED;
+        mMenuItems = null;
+        mMenuItemClickListener = null;
+        mFloatingToolbarToken = 0;
+        mSuggestedWidth = 0;
+        mWidthChanged = true;
+        resetCoords();
+        mPreviousContentRect.setEmpty();
+        mScreenViewPort.setEmpty();
+        mPopupWindow.dismiss();
+    }
+
+    private void resetCoords() {
+        mCoordsOnScreen[0] = 0;
+        mCoordsOnScreen[1] = 0;
+        mCoordsOnWindow[0] = 0;
+        mCoordsOnWindow[1] = 0;
+    }
+
+    private void runOnUiThread(Runnable runnable) {
+        mParent.post(runnable);
+    }
+
+    private void onShow(WidgetInfo info) {
+        runOnUiThread(() -> {
+            mFloatingToolbarToken = info.getWidgetToken();
+            mState = TOOLBAR_STATE_SHOWN;
+            updatePopupWindowContent(info);
+            Rect contentRect = info.getContentRect();
+            mPopupWindow.setWidth(contentRect.width());
+            mPopupWindow.setHeight(contentRect.height());
+            final Point coords = getCoordinatesInWindow(contentRect.left, contentRect.top);
+            mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, coords.x, coords.y);
+        });
+    }
+
+    private void onWidgetUpdated(WidgetInfo info) {
+        runOnUiThread(() -> {
+            if (!isShowing()) {
+                Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "onWidgetUpdated(): The widget isn't showing.");
+                return;
+            }
+            updatePopupWindowContent(info);
+            Rect contentRect = info.getContentRect();
+            Point coords = getCoordinatesInWindow(contentRect.left, contentRect.top);
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "PopupWindow x= " + coords.x + " y= " + coords.y + " w="
+                                + contentRect.width() + " h=" + contentRect.height());
+            }
+            mPopupWindow.update(coords.x, coords.y, contentRect.width(), contentRect.height());
+        });
+    }
+
+    private void onToolbarShowTimeout() {
+        runOnUiThread(() -> {
+            if (mState == TOOLBAR_STATE_DISMISSED) {
+                return;
+            }
+            doDismissPopupWindow();
+        });
+    }
+
+    private void onMenuItemClicked(ToolbarMenuItem toolbarMenuItem) {
+        runOnUiThread(() -> {
+            if (mMenuItems == null || mMenuItemClickListener == null) {
+                return;
+            }
+            MenuItem item = getMenuItemByToolbarMenuItem(toolbarMenuItem);
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "SelectionToolbarCallbackImpl onMenuItemClicked. toolbarMenuItem="
+                                + toolbarMenuItem + " item=" + item);
+            }
+            // TODO: handle the menu item like clipboard
+            if (item != null) {
+                mMenuItemClickListener.onMenuItemClick(item);
+            } else {
+                Log.e(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "onMenuItemClicked: cannot find menu item.");
+            }
+        });
+    }
+
+    private static class SelectionToolbarCallbackImpl extends ISelectionToolbarCallback.Stub {
+
+        private final WeakReference<RemoteFloatingToolbarPopup> mRemotePopup;
+
+        SelectionToolbarCallbackImpl(RemoteFloatingToolbarPopup popup) {
+            mRemotePopup = new WeakReference<>(popup);
+        }
+
+        @Override
+        public void onShown(WidgetInfo info) {
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "SelectionToolbarCallbackImpl onShown: " + info);
+            }
+            final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
+            if (remoteFloatingToolbarPopup != null) {
+                remoteFloatingToolbarPopup.onShow(info);
+            } else {
+                Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "Lost remoteFloatingToolbarPopup reference for onShown.");
+            }
+        }
+
+        @Override
+        public void onWidgetUpdated(WidgetInfo info) {
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "SelectionToolbarCallbackImpl onWidgetUpdated: info = " + info);
+            }
+            final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
+            if (remoteFloatingToolbarPopup != null) {
+                remoteFloatingToolbarPopup.onWidgetUpdated(info);
+            } else {
+                Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "Lost remoteFloatingToolbarPopup reference for onWidgetUpdated.");
+            }
+        }
+
+        @Override
+        public void onToolbarShowTimeout() {
+            final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
+            if (remoteFloatingToolbarPopup != null) {
+                remoteFloatingToolbarPopup.onToolbarShowTimeout();
+            } else {
+                Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "Lost remoteFloatingToolbarPopup reference for onToolbarShowTimeout.");
+            }
+        }
+
+        @Override
+        public void onMenuItemClicked(ToolbarMenuItem toolbarMenuItem) {
+            final RemoteFloatingToolbarPopup remoteFloatingToolbarPopup = mRemotePopup.get();
+            if (remoteFloatingToolbarPopup != null) {
+                remoteFloatingToolbarPopup.onMenuItemClicked(toolbarMenuItem);
+            } else {
+                Log.w(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "Lost remoteFloatingToolbarPopup reference for onMenuItemClicked.");
+            }
+        }
+
+        @Override
+        public void onError(int errorCode) {
+            if (DEBUG) {
+                Log.v(FloatingToolbar.FLOATING_TOOLBAR_TAG,
+                        "SelectionToolbarCallbackImpl onError: " + errorCode);
+            }
+        }
+    }
+
+    /**
+     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
+     */
+    static final class MenuItemRepr {
+
+        public final int mItemId;
+        public final int mGroupId;
+        @Nullable
+        public final String mTitle;
+        @Nullable private final Drawable mIcon;
+
+        private MenuItemRepr(
+                int itemId, int groupId, @Nullable CharSequence title,
+                @Nullable Drawable icon) {
+            mItemId = itemId;
+            mGroupId = groupId;
+            mTitle = (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(mItemId, mGroupId, mTitle, 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 LocalFloatingToolbarPopup.MenuItemRepr)) {
+                return false;
+            }
+            final MenuItemRepr other = (MenuItemRepr) o;
+            return mItemId == other.mItemId
+                    && mGroupId == other.mGroupId
+                    && TextUtils.equals(mTitle, other.mTitle)
+                    // 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/jni/OWNERS b/core/jni/OWNERS
index 9a460f5..24c0d2a 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -66,6 +66,7 @@
 
 ### Graphics ###
 per-file android_graphics_* = file:/graphics/java/android/graphics/OWNERS
+per-file android_hardware_HardwareBuffer.cpp = file:/graphics/java/android/graphics/OWNERS
 
 ### Text ###
 per-file android_text_* = file:/core/java/android/text/OWNERS
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index b25b4f7..f462523 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -85,7 +85,7 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer(width, height, pixelFormat, layers,
             grallocUsage, std::string("HardwareBuffer pid [") + std::to_string(getpid()) +"]");
     status_t error = buffer->initCheck();
-    if (error < 0) {
+    if (error != OK) {
         if (kDebugGraphicBuffer) {
             ALOGW("createGraphicBuffer() failed in HardwareBuffer.create()");
         }
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index 1be1873..9734ab9 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -61,27 +61,9 @@
   return (jint)res;
 }
 
-static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
-  int res = qtaguid_setCounterSet(setNum, uid);
-  if (res < 0) {
-    return (jint)-errno;
-  }
-  return (jint)res;
-}
-
-static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
-  int res = qtaguid_deleteTagData(tagNum, uid);
-  if (res < 0) {
-    return (jint)-errno;
-  }
-  return (jint)res;
-}
-
 static const JNINativeMethod gQTagUidMethods[] = {
   { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
   { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
-  { "native_setCounterSet", "(II)I", (void*)setCounterSet},
-  { "native_deleteTagData", "(II)I", (void*)deleteTagData},
 };
 
 int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 81d849e..97870a1 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -22,6 +22,13 @@
 
 import "frameworks/base/core/proto/android/privacy.proto";
 
+message AllSensorPrivacyServiceDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Is global sensor privacy enabled
+    optional bool is_enabled = 1;
+}
+
 message SensorPrivacyServiceDumpProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -35,6 +42,9 @@
 
     // Per user settings for sensor privacy
     repeated SensorPrivacyUserProto user = 3;
+
+    // Implementation
+    optional string storage_implementation = 4;
 }
 
 message SensorPrivacyUserProto {
@@ -43,16 +53,47 @@
     // User id
     optional int32 user_id = 1;
 
+    // DEPRECATED
     // Is global sensor privacy enabled
     optional bool is_enabled = 2;
 
     // Per sensor privacy enabled
+    // DEPRECATED
     repeated SensorPrivacyIndividualEnabledSensorProto individual_enabled_sensor = 3;
+
+    // Per toggle type sensor privacy
+    repeated SensorPrivacySensorProto sensors = 4;
+}
+
+message SensorPrivacySensorProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    enum Sensor {
+        UNKNOWN = 0;
+
+        MICROPHONE = 1;
+        CAMERA = 2;
+    }
+
+    optional int32 sensor = 1;
+
+    repeated SensorPrivacyIndividualEnabledSensorProto toggles = 2;
 }
 
 message SensorPrivacyIndividualEnabledSensorProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
+    enum ToggleType {
+        SOFTWARE = 1;
+        HARDWARE = 2;
+    }
+
+    enum StateType {
+        ENABLED = 1;
+        DISABLED = 2;
+    }
+
+    // DEPRECATED
     enum Sensor {
         UNKNOWN = 0;
 
@@ -63,8 +104,17 @@
     // Sensor for which privacy might be enabled
     optional Sensor sensor = 1;
 
-    // If sensor privacy is enabled for this sensor
+    // DEPRECATED
     optional bool is_enabled = 2;
+
+    // Timestamp of the last time the sensor was changed
+    optional int64 last_change = 3;
+
+    // The toggle type for this state
+    optional ToggleType toggle_type = 4;
+
+    // If sensor privacy state for this sensor
+    optional StateType state_type = 5;
 }
 
 message SensorPrivacyToggleSourceProto {
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index acb7429..d48ea3b 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -127,7 +127,7 @@
     optional bool is_light_device_idle_mode = 25;
     // True if we are currently in device idle mode.
     optional bool is_device_idle_mode = 26;
-    // Set of app ids that we will always respect the wake locks for.
+    // Set of app ids that we will respect the wake locks for while in device idle mode.
     repeated int32 device_idle_whitelist = 27;
     // Set of app ids that are temporarily allowed to acquire wakelocks due to
     // high-pri message
@@ -187,6 +187,8 @@
     // Whether or not the current enhanced discharge prediction is personalized based on device
     // usage or not.
     optional bool is_enhanced_discharge_prediction_personalized = 54;
+    optional bool is_low_power_standby_active = 55;
+    optional LowPowerStandbyControllerDumpProto low_power_standby_controller = 56;
 }
 
 // A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
@@ -209,6 +211,8 @@
         // When this wake lock is released, poke the user activity timer
         // so the screen stays on for a little longer.
         optional bool is_on_after_release = 2;
+        // The wakelock is held by the system server on request by another app.
+        optional bool system_wakelock = 3;
     }
 
     optional .android.os.WakeLockLevelEnum lock_level = 1;
@@ -428,3 +432,39 @@
 
     // Next tag: 23
 }
+
+message LowPowerStandbyControllerDumpProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // True if Low Power Standby is active
+    optional bool is_active = 1;
+
+    // True if Low Power Standby is enabled
+    optional bool is_enabled = 2;
+
+    // True if Low Power Standby is supported
+    optional bool is_supported_config = 3;
+
+    // True if Low Power Standby is enabled by default
+    optional bool is_enabled_by_default_config = 4;
+
+    // True if the device is currently interactive
+    optional bool is_interactive = 5;
+
+    // Time (in elapsedRealtime) when the device was last interactive
+    optional bool last_interactive_time = 6;
+
+    // Time (in milliseconds) after becoming non-interactive that Low Power Standby can activate
+    optional int32 standby_timeout_config = 7;
+
+    // True if the device has entered idle mode since becoming non-interactive
+    optional int32 idle_since_non_interactive = 8;
+
+    // True if the device is currently in idle mode
+    optional int32 is_device_idle = 9;
+
+    // Set of app ids that are exempt form low power standby
+    repeated int32 allowlist = 10;
+
+    // Next tag: 11
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8cf5421..c9bd222 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -107,6 +107,7 @@
     <protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
+    <protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED" />
     <protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
 
     <!-- @deprecated This is rarely used and will be phased out soon. -->
@@ -715,7 +716,7 @@
     <protected-broadcast android:name="android.app.action.SHOW_NEW_USER_DISCLAIMER" />
 
     <!-- Added in T -->
-    <protected-broadcast android:name="android.intent.action.REFRESH_SAFETY_SOURCES" />
+    <protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" />
     <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
     <protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" />
 
@@ -1838,11 +1839,12 @@
     <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi @hide Allows automotive applications to control location
+    <!-- @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+         Allows automotive applications to control location
          suspend state for power management use cases.
          <p>Not for use by third-party applications.
     -->
-    <permission android:name="android.permission.AUTOMOTIVE_GNSS_CONTROLS"
+    <permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS"
         android:protectionLevel="signature|privileged" />
 
     <!-- ======================================= -->
@@ -3661,6 +3663,13 @@
     <permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- @hide @SystemApi Must be required by a
+         {@link com.android.service.tracing.TraceReportService}, to ensure that only the system
+         can bind to it.
+        <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.BIND_TRACE_REPORT_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @hide @SystemApi @TestApi
          Allow an application to approve incident and bug reports to be
          shared off-device.  There can be only one application installed on the
@@ -4955,6 +4964,11 @@
     <permission android:name="android.permission.USER_ACTIVITY"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @hide @SystemApi Allows an application to manage Low Power Standby settings.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MANAGE_LOW_POWER_STANDBY"
+                android:protectionLevel="signature|privileged" />
+
    <!-- @hide Allows low-level access to tun tap driver -->
     <permission android:name="android.permission.NET_TUNNELING"
         android:protectionLevel="signature" />
@@ -6090,13 +6104,13 @@
     <permission android:name="android.permission.ACCESS_TV_SHARED_FILTER"
         android:protectionLevel="signature|privileged|vendorPrivileged" />
 
-    <!-- Allows an application to create trusted displays. @hide -->
+    <!-- Allows an application to create trusted displays. @hide @SystemApi -->
     <permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
-    <!-- Allows an application to create always-unlocked displays. @hide -->
+    <!-- Allows an application to create always-unlocked displays. @hide @SystemApi -->
     <permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"
-                android:protectionLevel="signature"/>
+                android:protectionLevel="signature|role"/>
 
     <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
@@ -6140,6 +6154,11 @@
     <permission android:name="android.permission.MANAGE_GAME_MODE"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- @TestApi Allows setting the game service provider, meant for tests only.
+     @hide -->
+    <permission android:name="android.permission.SET_GAME_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows accessing the frame rate per second of a given application
      @hide -->
     <permission android:name="android.permission.ACCESS_FPS_COUNTER"
diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
new file mode 100644
index 0000000..cce9593
--- /dev/null
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -0,0 +1,115 @@
+<?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.
+-->
+
+<!-- NOTE: outer layout is required to provide proper shadow. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/autofill_dialog_picker"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
+    android:padding="@dimen/autofill_save_outer_top_padding"
+    android:elevation="@dimen/autofill_elevation"
+    android:background="?android:attr/colorBackground"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:paddingStart="@dimen/autofill_save_inner_padding"
+        android:paddingEnd="@dimen/autofill_save_inner_padding"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/autofill_service_icon"
+            android:scaleType="fitStart"
+            android:visibility="gone"
+            android:layout_width="@dimen/autofill_dialog_icon_size"
+            android:layout_height="@dimen/autofill_dialog_icon_size"/>
+
+        <LinearLayout
+            android:id="@+id/autofill_dialog_header"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:paddingStart="@dimen/autofill_save_inner_padding"
+            android:paddingEnd="@dimen/autofill_save_inner_padding"
+            android:visibility="gone"
+            android:foreground="?attr/listChoiceBackgroundIndicator"
+            style="@style/AutofillDatasetPicker" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/autofill_dialog_container"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:paddingStart="@dimen/autofill_save_inner_padding"
+        android:paddingEnd="@dimen/autofill_save_inner_padding"
+        android:visibility="gone"
+        android:foreground="?attr/listChoiceBackgroundIndicator"
+        style="@style/AutofillDatasetPicker" />
+
+    <ListView
+        android:id="@+id/autofill_dialog_list"
+        android:layout_weight="1"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:drawSelectorOnTop="true"
+        android:clickable="true"
+        android:divider="@null"
+        android:visibility="gone"
+        android:paddingStart="@dimen/autofill_save_inner_padding"
+        android:paddingEnd="@dimen/autofill_save_inner_padding"
+        android:foreground="?attr/listChoiceBackgroundIndicator"
+        style="@style/AutofillDatasetPicker" />
+
+    <com.android.internal.widget.ButtonBarLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:padding="@dimen/autofill_save_button_bar_padding"
+        android:clipToPadding="false"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/autofill_dialog_no"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="?android:attr/buttonBarButtonStyle"
+            android:text="@string/dismiss_action">
+        </Button>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:visibility="invisible">
+        </Space>
+
+        <Button
+            android:id="@+id/autofill_dialog_yes"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="@style/Widget.DeviceDefault.Button.Colored"
+            android:text="@string/autofill_save_yes"
+            android:visibility="gone" >
+        </Button>
+
+    </com.android.internal.widget.ButtonBarLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/input_method_navigation_layout.xml b/core/res/res/layout/input_method_navigation_layout.xml
index 05e750a..e0f0143 100644
--- a/core/res/res/layout/input_method_navigation_layout.xml
+++ b/core/res/res/layout/input_method_navigation_layout.xml
@@ -19,8 +19,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginStart="@dimen/input_method_rounded_corner_content_padding"
-    android:layout_marginEnd="@dimen/input_method_rounded_corner_content_padding"
+    android:layout_marginStart="@dimen/rounded_corner_content_padding"
+    android:layout_marginEnd="@dimen/rounded_corner_content_padding"
     android:paddingStart="@dimen/input_method_nav_content_padding"
     android:paddingEnd="@dimen/input_method_nav_content_padding"
     android:clipChildren="false"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3bc0283..408054d 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Vee uit"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Invoermetode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Teksaksies"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Bergingspasie word min"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"verkry toegang tot SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Gee \'n program toegang tot SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index fb52e6a..3ac6c60 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ሰርዝ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ግቤት ስልት"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"የፅሁፍ እርምጃዎች"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"የማከማቻ ቦታ እያለቀ ነው"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"አንድ መተግበሪያ ለዚያ መተግበሪያ የባትሪ ማትባቶችን ችላ ለማለት እንዲጠይቅ ይፈቅድለታል።"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ሁሉንም ጥቅሎች ይጠይቁ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"አንድ መተግበሪያ ሁሉንም የተጫኑ ጥቅሎችን እንዲያይ ይፈቅድለታል።"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisን ይድረሱ"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"መተግበሪያ SupplementalApisን እንዲደርስ ይፈቅዳል።"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ምግብር ማከል አልተቻለም።"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ሂድ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 12d1b83..018595f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1267,6 +1267,10 @@
     <string name="deleteText" msgid="4200807474529938112">"حذف"</string>
     <string name="inputMethod" msgid="1784759500516314751">"طريقة الإرسال"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"إجراءات النص"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"مساحة التخزين منخفضة"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"قد لا تعمل بعض وظائف النظام"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1571,6 +1575,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"للسماح للتطبيق بطلب الإذن لتجاهل تحسينات البطارية في هذا التطبيق."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"طلب البحث في كل الحِزم"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"يسمح هذا الإذن للتطبيق بعرض كل الحِزم المثبّتة."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏الوصول إلى SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏السماح لأحد التطبيقات بالوصول إلى SupplementalApis"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"اضغط مرتين للتحكم في التكبير أو التصغير"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"تعذرت إضافة أداة."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"تنفيذ"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f57e42b..6db3b18 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"মচক"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ইনপুট পদ্ধতি"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"পাঠ বিষয়ক কাৰ্য"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ষ্ট’ৰেজৰ খালী ঠাই শেষ হৈ আছে"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এমবি খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপ্‌টোৰ বাবে বেটাৰী অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"আটাইবোৰ পেকেজত প্ৰশ্ন সোধক"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এপক আটাইবোৰ ইনষ্টল কৰি থোৱা পেকেজ চাবলৈ দিয়ে।"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis এক্সেছ কৰক"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"কোনো এপ্লিকেশ্বনক SupplementalApis এক্সেছ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ৱিজেট যোগ কৰিব পৰা নগ\'ল।"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"যাওক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 76384f9..a6e2aab 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Sil"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Daxiletmə metodu"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Mətn əməliyyatları"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Yaddaş yeri bitir"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bəzi sistem funksiyaları işləməyə bilər"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Tatareya optimallaşdırılmasını o tətbiq üçün iqnor edilməsinə icazə vermək məqsədilə soruşmağa tətbiqə icazə verilir."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"bütün paketlər üçün sorğu göndərin"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tətbiqə bütün quraşdırılmış paketləri görmək icazəsi verir."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'ə giriş"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Tətbiqə SupplementalApis\'ə giriş icazəsi verir."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zoom kontrolu üçün iki dəfə toxunun"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget əlavə edilə bilmədi."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Get"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 15e3aa0..0c06851a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metod unosa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Radnje u vezi sa tekstom"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memorijski prostor je na izmaku"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda ne funkcionišu"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Dozvoljava aplikaciji da traži dozvolu za ignorisanje optimizacija baterije za tu aplikaciju."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Dozvoljava aplikaciji da vidi sve instalirane pakete."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristup stavci SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Dozvoljava aplikaciji da pristupa stavci SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu zumiranja"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nije moguće dodati vidžet."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e182c28..ebcc13d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Выдалiць"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метад уводу"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Дзеянні з тэкстам"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Месца для захавання на зыходзе"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дазваляе праграме запытваць дазвол на ігнараванне аптымізацыі акумулятара для гэтай праграмы."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"запыт усіх пакетаў"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дазваляе праграме выяўляць усе ўсталяваныя пакеты."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"доступ да SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дазваляе праграме мець доступ да SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Націсніце двойчы, каб кіраваць маштабаваннем"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Немагчыма дадаць віджэт."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Пачаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 41aa9f7..8cd3ef13 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Изтриване"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метод на въвеждане"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Действия с текста"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Мястото в хранилището е на изчерпване"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Възможно е някои функции на системата да не работят"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешава на дадено приложение да иска разрешение за пренебрегване на свързаните с него оптимизации на батерията."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"заявка за всички пакети"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Разрешава на приложението да вижда всички инсталирани пакети."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"достъп до SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Позволява на приложението достъп до SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Докоснете двукратно за управление на промяната на мащаба"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Приспособлението не можа да бъде добавено."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Старт"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 7dac240d..13673f4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"মুছুন"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ইনপুট পদ্ধতি"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"পাঠ্য ক্রিয়াগুলি"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো অ্যাপের জন্য ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করতে সেটিকে অনুমতির চাওয়ার মঞ্জুরি দেয়৷"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"অন্যান্য সব প্যাকেজের তথ্য সম্পর্কিত কোয়েরি দেখুন"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এটি কোনও অ্যাপকে সর্বদা ইনস্টল করা সব প্যাকেজ দেখতে অনুমতি দেয়।"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis অ্যাক্সেস করুন"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis অ্যাক্সেস করতে অ্যাপে অনুমতি দেয়।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্রণের জন্য দুবার ট্যাপ করুন"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"উইজেট যোগ করা যায়নি৷"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 28ca86d..6b1afb2 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Način unosa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Akcije za tekst"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke funkcije sistema možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Omogućava aplikaciji da traži odobrenje za zanemarivanje optimizacije baterije za tu aplikaciju."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"upit za sve pakete"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Omogućava aplikaciji da pregleda sve instalirane pakete."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristup za SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Dozvoljava aplikaciji pristup za SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu uvećanja"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Dodavanje vidžeta nije uspjelo."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Započni"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index be1b43c..3a1baf9 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Suprimeix"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Mètode d\'introducció de text"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Accions de text"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"És possible que algunes funcions del sistema no funcionin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet que una aplicació demani permís per ignorar les optimitzacions de bateria per a l\'aplicació."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar tots els paquets"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet que una aplicació vegi els paquets instal·lats."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accedir a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permet que una aplicació accedeixi a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Piqueu dos cops per controlar el zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No s\'ha pogut afegir el widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ves"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d038dfc..4936836 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Smazat"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metoda zadávání dat"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Operace s textem"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"V úložišti je málo místa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Některé systémové funkce nemusí fungovat"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Povoluje aplikaci požádat o oprávnění ignorovat optimalizaci využití baterie, která pro ni je nastavena."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zjistit všechny balíčky"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Umožňuje aplikaci načíst všechny nainstalované balíčky."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"přístup k rozhraním SupplementalApi"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Umožňuje aplikaci přístup k rozhraním SupplementalApi."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Poklepáním můžete ovládat přiblížení"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nelze přidat."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Přejít"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index afe1175..eed13d4 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Slet"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Inputmetode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Teksthandlinger"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der er snart ikke mere lagerplads"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nogle systemfunktioner virker måske ikke"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gør det muligt for en app at bede om tilladelse til at ignorere batterioptimeringer for den pågældende app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"forespørg om alle pakker"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Giver en app tilladelse til at se alle installerede pakker."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"adgang til SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Giver en app adgang til SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tryk to gange for zoomkontrol"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget kunne ikke tilføjes."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Gå"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ecaa63c..1bd20bd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Löschen"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Eingabemethode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Textaktionen"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der Speicherplatz wird knapp"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Einige Systemfunktionen funktionieren eventuell nicht."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -1491,6 +1495,10 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Alle Pakete abfragen"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ermöglicht der App, alle installierten Pakete zu sehen."</string>
+    <!-- no translation found for permlab_accessSupplementalApi (3544659160536960275) -->
+    <skip />
+    <!-- no translation found for permdesc_accessSupplementalApi (8974758769370951074) -->
+    <skip />
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Für Zoomeinstellung zweimal berühren"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget konnte nicht hinzugefügt werden."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Los"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 44fe426..52ab901 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Διαγραφή"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Μέθοδος εισόδου"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Ενέργειες κειμένου"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Επιτρέπει σε μια εφαρμογή να ζητήσει άδεια για την αγνόηση βελτιστοποιήσεων της μπαταρίας για τη συγκεκριμένη εφαρμογή."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"υποβολή ερωτήματος σε όλα τα πακέτα"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Επιτρέπει σε μια εφαρμογή να βλέπει όλα τα εγκατεστημένα πακέτα."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"πρόσβαση στο SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Επιτρέπει σε μια εφαρμογή να έχει πρόσβαση στο SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Πατήστε δύο φορές για έλεγχο εστίασης"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Μετάβαση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a23eda2..9a40fa6 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Delete"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 281ecb1..1a2e8d9 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Delete"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 8d8b33c..3039233 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Delete"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e411655..0e93f31 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Delete"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Input method"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Text actions"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Allows an application to access SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index c2e7472..db0f559 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1187,6 +1187,8 @@
     <string name="deleteText" msgid="4200807474529938112">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎Delete‎‏‎‎‏‎"</string>
     <string name="inputMethod" msgid="1784759500516314751">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎Input method‎‏‎‎‏‎"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎Text actions‎‏‎‎‏‎"</string>
+    <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎Back‎‏‎‎‏‎"</string>
+    <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎Switch input method‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎Storage space running out‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎Some system functions may not work‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎Not enough storage for the system. Make sure you have 250MB of free space and restart.‎‏‎‎‏‎"</string>
@@ -1491,6 +1493,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎Allows an app to ask for permission to ignore battery optimizations for that app.‎‏‎‎‏‎"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎query all packages‎‏‎‎‏‎"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎Allows an app to see all installed packages.‎‏‎‎‏‎"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎access SupplementalApis‎‏‎‎‏‎"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎Allows an application to access SupplementalApis.‎‏‎‎‏‎"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎Tap twice for zoom control‎‏‎‎‏‎"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎Couldn\'t add widget.‎‏‎‎‏‎"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‎Go‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index fad71eb7..dc8f357 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio de almacenamiento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no estén disponibles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una app solicite permiso para ignorar las optimizaciones de la batería."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Búsqueda de todos los paquetes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una app vea todos los paquetes que se instalaron."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Acceder a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que una aplicación acceda a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Presiona dos veces para obtener el control del zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se pudo agregar el widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 669e914..ec17d22 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de introducción de texto"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no funcionen."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1264,7 +1268,7 @@
     <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Para volver a habilitar esta opción, accede a Ajustes &gt; Aplicaciones &gt; Descargadas."</string>
     <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite el tamaño de pantalla actual y es posible que funcione de forma inesperada."</string>
     <string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar siempre"</string>
-    <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> se diseñó para una versión incompatible de Android OS y puede que funcione de forma inesperada. Es posible que haya una versión actualizada de la aplicación."</string>
+    <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> se diseñó para una versión incompatible del SO Android y puede que funcione de forma inesperada. Es posible que haya una versión actualizada de la aplicación."</string>
     <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Mostrar siempre"</string>
     <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Buscar actualizaciones"</string>
     <string name="smv_application" msgid="3775183542777792638">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode autoaplicable."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una aplicación solicite permiso para ignorar las optimizaciones de la batería."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos los paquetes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una aplicación vea todos los paquetes instalados."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acceder a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que una aplicación acceda a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Da dos toques para acceder al control de zoom."</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se ha podido añadir el widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index e4d33a7..30e78c1 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Kustuta"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Sisestusmeetod"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tekstitoimingud"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Talletusruum saab täis"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lubab rakendusel küsida luba rakenduse aku optimeerimise eiramiseks."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"päringute esitamine kõikide pakettide kohta"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Võimaldab rakendusel näha kõiki installitud pakette."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"juurdepääs üksusele SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Annab rakendusele juurdepääsu üksusele SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Suumi kasutamiseks koputage kaks korda"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidinat ei saanud lisada."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Mine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a3766b76..1c301e6 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Ezabatu"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Idazketa-metodoa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Testu-ekintzak"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memoria betetzen ari da"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bateriaren optimizazioei ez ikusi egiteko baimena eskatzea baimentzen die aplikazioei."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Kontsultatu pakete guztiak"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Instalatutako pakete guztiak ikusteko baimena ematen dio aplikazioari."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"atzitu SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis direlakoak (API osagarriak) atzitzeko baimena ematen die aplikazioei."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Sakatu birritan zooma kontrolatzeko"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ezin izan da widgeta gehitu."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Joan"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index c9dedf9..ca06cf9 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"حذف"</string>
     <string name="inputMethod" msgid="1784759500516314751">"روش ورودی"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"کنش‌های متنی"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"فضای ذخیره‌سازی درحال پر شدن است"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"فضای ذخیره‌سازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راه‌اندازی مجدد کنید."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"به یک برنامه اجازه می‌دهد جهت نادیده گرفتن بهینه‌سازی باتری برای خود مجوز درخواست کند."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"پُرسمان همه بسته‌ها"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"به برنامه اجازه می‌دهد همه بسته‌های نصب‌شده را ببیند."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏دسترسی به SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏به برنامه اجازه می‌دهد به SupplementalApis دسترسی داشته باشد."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگ‌نمایی، دو بار ضربه بزنید"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزارک انجام نشد."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"برو"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b7234be..6bac5ac 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Poista"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Syöttötapa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tekstitoiminnot"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Tallennustila loppumassa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Sallii sovelluksen pyytää lupaa ohittaa tietyn sovelluksen akun optimoinnit."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kaikkien pakettien näkeminen"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Antaa sovelluksen nähdä kaikki asennetut paketit."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pääsy SupplementalApi-rajapintoihin"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Sovellus saa pääsyn SupplementalApi-rajapintoihin."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Hallitse zoomausta napauttamalla kahdesti"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widgetin lisääminen epäonnistui."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Siirry"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6a08237..2db9e97 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Supprimer"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Mode de saisie"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Actions sur le texte"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet à une application de demander la permission d\'ignorer les optimisations de la pile."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"envoyer une requête à propos de tous les paquets"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet à une application de voir tous les paquets installés."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accès à SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Autorise une application à accéder à SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Aller"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c606ed3..bb28b02 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Supprimer"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Mode de saisie"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Actions sur le texte"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Autorise une application à demander l\'autorisation d\'ignorer les optimisations de batterie pour cette application."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"interroger tous les packages"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Autorise une appli à voir tous les packages installés."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"accéder à SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Autorise une application à accéder à SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 24287d9..47247bf 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de introdución de texto"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Accións de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Estase esgotando o espazo de almacenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"É posible que algunhas funcións do sistema non funcionen"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os paquetes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que unha aplicación consulte todos os paquetes instalados."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acceso a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que unha aplicación acceda a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toca dúas veces para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Non se puido engadir o widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5ca2c39..b0efeab 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ડિલીટ કરો"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ઇનપુટ પદ્ધતિ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ટેક્સ્ટ ક્રિયાઓ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ઍપ્લિકેશનને તે ઍપ્લિકેશન માટે બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવાની પરવાનગી આપવા માટે પૂછવાની મંજૂરી આપે છે."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"બધા પૅકેજ જુઓ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"કોઈ ઍપને ઇન્સ્ટૉલ કરેલા બધા પૅકેજ જોવાની મંજૂરી આપે છે."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisને ઍક્સેસ કરો"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"કોઈ ઍપ્લિકેશનને SupplementalApis ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"વિજેટ ઉમેરી શકાયું નથી."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"જાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b846ff8..70d0270 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"मिटाएं"</string>
     <string name="inputMethod" msgid="1784759500516314751">"इनपुट विधि"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"लेख क्रियाएं"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"मेमोरी में जगह नहीं बची है"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"हो सकता है कुछ सिस्टम फ़ंक्शन काम नहीं करें"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"किसी ऐप्लिकेशन को उस ऐप्लिकेशन के लिए बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने की अनुमति के लिए पूछने देता है."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखें"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यह किसी ऐप्लिकेशन को, डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखने की अनुमति देता है."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis का ऐक्सेस पाना"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"इससे, किसी ऐप्लिकेशन को SupplementalApis को ऐक्सेस करने की अनुमति मिलती है."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ज़ूम नियंत्रण के लिए दो बार टैप करें"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट नहीं जोड़ा जा सका."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"जाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e88b331..d48713f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Način unosa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Radnje s tekstom"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji omogućuje da traži dopuštenje za zanemarivanje optimizacija baterije za tu aplikaciju."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji omogućuje pregled instaliranih paketa."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pristupiti SupplementalApijima"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Omogućuje aplikaciji pristupanje SupplementalApijima."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvaput dotaknite za upravljanje zumiranjem"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nije moguće dodati."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 46fcc2a..5d3fa9f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Törlés"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Beviteli mód"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Műveletek szöveggel"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kevés a szabad terület"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Az alkalmazás engedélyt kérhet az akkumulátoroptimalizálási beállítások mellőzésére."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"az összes csomag lekérdezése"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Engedélyezi az adott alkalmazás számára, hogy lássa az összes telepített csomagot."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"hozzáférés ehhez: SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Lehetővé teszi egy alkalmazás számára, hogy hozzáférjen a következőhöz: SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Érintse meg kétszer a nagyítás beállításához"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nem sikerült hozzáadni a modult."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ugrás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b60f08b..8b6ef09 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Ջնջել"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Մուտքագրման եղանակը"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Տեքստի գործողությունները"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Հիշողությունը սպառվում է"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Որոշ գործառույթներ կարող են չաշխատել"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Հավելվածին հնարավորություն է տալիս հայցելու թույլտվություն՝ տվյալ հավելվածի համար մարտկոցի օպտիմալացումն անտեսելու համար:"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"հարցում բոլոր փաթեթների համար"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Թույլ է տալիս հավելվածին տեսնել բոլոր տեղադրված փաթեթները։"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"օգտվել SupplementalApis-ից"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Թույլ է տալիս հավելվածին օգտվել SupplementalApis-ից։"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Հպեք երկու անգամ` խոշորացման վերահսկման համար"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Չհաջողվեց վիջեթ ավելացնել:"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Առաջ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 7925b3f..10bfa5e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Hapus"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metode masukan"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tindakan teks"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang penyimpanan hampir habis"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Mengizinkan aplikasi meminta izin untuk mengabaikan pengoptimalan baterai bagi aplikasi tersebut."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"mengkueri semua paket"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Mengizinkan aplikasi melihat semua paket yang diinstal."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"akses SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Mengizinkan aplikasi mengakses SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketuk dua kali untuk kontrol perbesar/perkecil"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Buka"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c758de5..79c2c68 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Eyða"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Innsláttaraðferð"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Textaaðgerðir"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Geymslurýmið er senn á þrotum"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gerir forriti kleift að biðja um heimild til að hunsa rafhlöðusparnað fyrir forritið."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"spyrja fyrir alla pakka"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Leyfir forriti að sjá alla uppsetta pakka."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"aðgangur að SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Veitir forriti aðgang að SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ýttu tvisvar til að opna aðdráttarstýringar"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ekki tókst að bæta græju við."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Áfram"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index c5683fa..a27a092 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Elimina"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metodo inserimento"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Azioni testo"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spazio di archiviazione in esaurimento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Alcune funzioni di sistema potrebbero non funzionare"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Consente a un\'app di chiedere l\'autorizzazione a ignorare le ottimizzazioni della batteria per quell\'app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Invio di query per tutti i pacchetti"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Consente a un\'app di visualizzare tutti i pacchetti installati."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Accesso a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Consente a un\'applicazione di accedere a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocca due volte per il comando dello zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Aggiunta del widget non riuscita."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Vai"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9da03ea..c234ea1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"מחיקה"</string>
     <string name="inputMethod" msgid="1784759500516314751">"שיטת קלט"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"פעולות טקסט"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"מקום האחסון עומד להיגמר"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏אין מספיק מקום אחסון עבור המערכת. עליך לוודא שיש לך מקום פנוי בנפח של 250MB ולהתחיל שוב."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"מאפשרת לאפליקציה לבקש רשות להתעלם מאופטימיזציות של הסוללה לאפליקציה הזו."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"שליחת שאילתות לכל החבילות"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"מאפשרת לאפליקציה לראות את כל החבילות המותקנות."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏גישה אל SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏מאפשרת לאפליקציה לגשת אל SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"יש להקיש פעמיים לשינוי המרחק מהתצוגה"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"‏לא ניתן להוסיף widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"התחלה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 76d248c..736fca6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"削除"</string>
     <string name="inputMethod" msgid="1784759500516314751">"入力方法"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"テキスト操作"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"空き容量わずか"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"一部のシステム機能が動作しない可能性があります"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"電池の最適化の無視についてアプリが確認することを許可します。"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"すべてのパッケージを照会する"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"すべてのインストール済みパッケージを参照することをアプリに許可します。"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis へのアクセス"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis へのアクセスをアプリに許可します。"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ダブルタップでズームします"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ウィジェットを追加できませんでした。"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"移動"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 91144c6..e0b14a9 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"წაშლა"</string>
     <string name="inputMethod" msgid="1784759500516314751">"შეყვანის მეთოდი"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ქმედებები ტექსტზე"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"თავისუფალი ადგილი იწურება"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"საშუალებას მისცემს აპს, მოითხოვოს მასთან დაკავშირებული ბატარეის ოპტიმიზაციის იგნორირების ნებართვა."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ყველა პაკეტის მოთხოვნა"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"საშუალებას აძლევს აპს, ნახოს ყველა ინსტალირებული პაკეტი."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis-ზე წვდომა"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"აპლიკაციას SupplementalApis-ზე წვდომის საშუალებას აძლევს."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"მასშტაბის ცვლილებისთვის შეეხეთ ორჯერ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ვერ დაემატა ვიჯეტი."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"გადასვლა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3f75495..3a130bd 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Жою"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Енгізу әдісі"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Мәтін әрекеттері"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Жадта орын азайып барады"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Қолданба батареяны оңтайландыру әрекетін елемеуді сұрай алады."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"барлық бумаға сұрау жасау"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Қолданба барлық орнатылған буманы көре алады."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApi интерфейстерін пайдалану"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Қолданбаға SupplementalApi интерфейстерін пайдалануға мүмкіндік береді."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабтау параметрін басқару үшін екі рет түртіңіз"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетті қосу."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Өту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index ecae41a..bb25bae0 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"លុប"</string>
     <string name="inputMethod" msgid="1784759500516314751">"វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"សកម្មភាព​អត្ថបទ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"អស់​ទំហំ​ផ្ទុក"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"មិន​មាន​ទំហំ​ផ្ទុក​​គ្រប់​គ្រាន់​សម្រាប់​ប្រព័ន្ធ​។ សូម​ប្រាកដ​ថា​អ្នក​មាន​ទំហំ​ទំនេរ​ 250MB ហើយ​ចាប់ផ្ដើម​ឡើង​វិញ។"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំការអនុញ្ញាត ដើម្បីមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម។"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"សួរសំណួរអំពីកញ្ចប់ទាំងអស់"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"អនុញ្ញាតឱ្យកម្មវិធីមើលកញ្ចប់ដែលបានដំឡើងទាំងអស់។"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"ចូលប្រើ SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"អនុញ្ញាតឱ្យកម្មវិធីចូលប្រើ SupplementalApis។"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ប៉ះ ពីរ​ដង​ដើម្បី​ពិនិត្យ​ការ​ពង្រីក"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"មិន​អាច​បន្ថែម​ធាតុ​ក្រាហ្វិក។"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ទៅ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a24166c..06075ac 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ಅಳಿಸಿ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ಇನ್‌ಪುಟ್ ವಿಧಾನ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ಪಠ್ಯದ ಕ್ರಮಗಳು"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ಈ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್‌ಗಳನ್ನು ಕಡೆಗಣಿಸುವುದಕ್ಕೆ ಅನುಮತಿಯನ್ನು ಕೇಳಲು ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್‌ಗಳ ಕುರಿತಾದ ಮಾಹಿತಿಯನ್ನು ಕೇಳಿ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದ ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ಅನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ಝೂಮ್‌ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ಹೋಗು"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8920be2..bbfae68 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"삭제"</string>
     <string name="inputMethod" msgid="1784759500516314751">"입력 방법"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"텍스트 작업"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"저장 공간이 부족함"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"앱에서 배터리 최적화를 무시할 수 있는 권한을 요청할 수 있도록 허용합니다."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"모든 패키지 쿼리"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"앱이 설치된 패키지를 모두 볼 수 있도록 허용합니다."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis 액세스"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"애플리케이션에서 SupplementalApis에 액세스하도록 허용합니다."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"확대/축소하려면 두 번 탭하세요."</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"위젯을 추가할 수 없습니다."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"이동"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 50c9393..aa7f5aa 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Жок кылуу"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Колдонмо батареянын кубатын керектегенден мурун уруксат суралсын."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"бардык топтомдор боюнча сурам жөнөтүү"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Колдонмо бардык орнотулган топтомдорду көрөт."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'ке кирүү"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Колдонмого SupplementalApis\'ке кирүү уруксатын берет."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетти кошуу мүмкүн болбоду."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Өтүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 398b6b3..fc68211 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ລຶບ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ການເຮັດວຽກຂອງຂໍ້ຄວາມ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"​ບໍ່​ມີ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ພຽງ​ພໍ​ສຳ​ລັບ​ລະ​ບົບ. ກວດ​ສອບ​ໃຫ້​ແນ່​ໃຈ​ວ່າ​ທ່ານ​ມີ​ພື້ນ​ທີ່​ຫວ່າງ​ຢ່າງ​ໜ້ອຍ 250MB ​ແລ້ວລອງ​ໃໝ່."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ອະນຸຍາດໃຫ້ແອັບຖາມສິດອະນຸຍາດເພື່ອເພີກເສີຍຕໍ່ການປັບແຕ່ງແບັດເຕີຣີສຳລັບແອັບນັ້ນ."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ຊອກຫາແພັກເກດທັງໝົດ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ອະນຸຍາດໃຫ້ແອັບເບິ່ງເຫັນແພັກເກດທີ່ຕິດຕັ້ງແລ້ວທັງໝົດໄດ້."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"ເຂົ້າເຖິງ SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນເຂົ້າເຖິງ SupplementalApis ໄດ້."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ແຕະສອງເທື່ອເພື່ອຄວບຄຸມການຊູມ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ບໍ່ສາມາດເພີ່ມວິດເຈັດໄດ້."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ໄປ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index de36cb6..ef7b0f1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Ištrinti"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Įvesties būdas"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Teksto veiksmai"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Mažėja laisvos saugyklos vietos"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kai kurios sistemos funkcijos gali neveikti"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Programai leidžiama prašyti leidimo nepaisyti tai programai skirto akumuliatoriaus optimizavimo nustatymų."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Teikti visų paketų užklausą"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Programai leidžiama peržiūrėti visus įdiegtus paketus."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"pasiekti papildomas API"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Leidžiama programai pasiekti papildomas API."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Bakstelėkite du kartus, kad valdytumėte mastelio keitimą"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nepavyko pridėti."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Pradėti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 93414e0..68725cb 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -982,7 +982,7 @@
     <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Logrīks <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ir izdzēsts."</string>
     <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Izvērst atbloķēšanas apgabalu."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Autorizācija, velkot ar pirkstu."</string>
-    <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Autorizācija ar kombināciju."</string>
+    <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Atbloķēšanas kombinācija."</string>
     <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Autorizācija pēc sejas."</string>
     <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Autorizācija ar PIN kodu."</string>
     <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM kartes atbloķēšanas PIN"</string>
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Dzēst"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Ievades metode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Teksta darbības"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Paliek maz brīvas vietas"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Dažas sistēmas funkcijas var nedarboties."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ļauj lietotnei lūgt atļauju ignorēt akumulatora optimizāciju šai lietotnei."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"pieprasīt atļauju skatīt visas pakotnes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ļauj lietotnei skatīt visas instalētās pakotnes."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"piekļuve SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ļauj lietojumprogrammai piekļūt SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nevarēja pievienot logrīku."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Doties uz"</string>
@@ -1917,7 +1923,7 @@
     <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2. darba profils: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3. darba profils: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Prasīt PIN kodu pirms atspraušanas"</string>
-    <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pirms atspraušanas pieprasīt grafisko atsl."</string>
+    <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pirms atspraušanas pieprasīt atbloķēšanas kombināciju"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pirms atspraušanas pieprasīt paroli"</string>
     <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalēja administrators"</string>
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d4f099f..1ccfc7e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метод на внес"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Дејства со текст"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Капацитетот е речиси полн"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некои системски функции може да не работат"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 MB и рестартирајте."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"пребарување на сите пакети"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволува апликацијата да ги гледа сите инсталирани пакети."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"пристап до SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дозволува апликацијата да пристапува до SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Допрете двапати за контрола на зумот"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не може да се додаде виџет."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Оди"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1c98f25..77540e1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ഇല്ലാതാക്കുക"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ടൈപ്പുചെയ്യൽ രീതി"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ടെക്‌സ്‌റ്റ് പ്രവർത്തനങ്ങൾ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"സംഭരണയിടം കഴിഞ്ഞു"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"സിസ്‌റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ആപ്പിന് വേണ്ടിയുള്ള ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകളെ അവഗണിക്കാനുള്ള അനുമതി ചോദിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"എല്ലാ പാക്കേജുകളും നോക്കുക"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ഇൻസ്‌റ്റാൾ ചെയ്‌ത എല്ലാ പാക്കേജുകളും കാണാൻ ഒരു ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ആക്‌സസ് ചെയ്യുക"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis ആക്‌സസ് ചെയ്യാൻ ഒരു ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"പോവുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index db67e90..dcb4859 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Устгах"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Оруулах арга"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Текст үйлдэл"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сангийн хэмжээ дутагдаж байна"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Зарим систем функц ажиллахгүй байна"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Тухайн аппaaс батерейны оновчлол алгасах зөвшөөрөл асуухыг зөвшөөрдөг."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"бүх багцыг лавлах"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Аппап бүх суулгасан багцыг харахыг зөвшөөрнө."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis-д хандах"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Аппликэйшнд SupplementalApis-д хандах зөвшөөрлийг олгодог."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Өсгөх контрол дээр хоёр удаа товшино уу"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджет нэмж чадсангүй."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Очих"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1385dcd..9cac6c8 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"हटवा"</string>
     <string name="inputMethod" msgid="1784759500516314751">"इनपुट पद्धत"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"मजकूर क्रिया"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"संचयन स्थान संपत आहे"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"त्या ॲपसाठी बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्‍यासाठी ॲपला परवानगी मागण्याची अनुमती देते."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"सर्व पॅकेजविषयी क्वेरी करा"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ॲपला इंस्टॉल केलेले सर्व पॅकेज पाहण्याची अनुमती देते."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis अ‍ॅक्सेस करा"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"अ‍ॅप्लिकेशनला SupplementalApis अ‍ॅक्सेस करण्याची अनुमती देते."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट जोडू शकलो नाही."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"जा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 43847fe..1f5b818 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Padam"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Kaedah input"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tindakan teks"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang storan semakin berkurangan"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Membenarkan apl meminta kebenaran untuk mengabaikan pengoptimuman bateri untuk apl itu."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buat pertanyaan untuk semua pakej"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Membenarkan apl melihat semua pakej yang dipasang."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"akses SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Membenarkan aplikasi mengakses SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketik dua kali untuk mendapatkan kawalan zum"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Pergi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index f509906..b244af5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ဖျက်ရန်"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ထည့်သွင်းရန်နည်းလမ်း"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"စာတို လုပ်ဆောင်ချက်"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန်အတွက် ခွင့်ပြုချက်တောင်းရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ပက်ကေ့ဂျ်အားလုံးကို မေးမြန်းခြင်း"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ထည့်သွင်းထားသော ပက်ကေ့ဂျ်အားလုံး ကြည့်ရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis သုံးခွင့်"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"အပလီကေးရှင်းအား SupplementalApis သုံးခွင့်ပေးနိုင်သည်။"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ဇူးမ်အသုံးပြုရန် နှစ်ချက်တို့ပါ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"သွားပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2ddd45d..a792d37 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Slett"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Inndatametode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Teksthandlinger"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lite ledig lagringsplass"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gjør det mulig for apper å be om tillatelse til å ignorere batterioptimaliseringer for disse appene."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"søk i alle pakker"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lar en app se alle installerte pakker."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"tilgang til SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Gir appen tilgang til SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trykk to ganger for zoomkontroll"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kunne ikke legge til modulen."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Utfør"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d4828b6..fe9c52b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"मेट्नुहोस्"</string>
     <string name="inputMethod" msgid="1784759500516314751">"निवेश विधि"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"पाठ कार्यहरू"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"भण्डारण ठाउँ सकिँदै छ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"कुनै एपलाई त्यसका ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्नाका लागि अनुमति माग्न दिन्छ।"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"सबै प्याकेजहरू खोज्नुहोस्"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यसले यस एपलाई इन्स्टल गरिएका सबै प्याकेजहरू हेर्ने अनुमति दिन्छ।"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis प्रयोग गर्ने अनुमति"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"एपलाई SupplementalApis प्रयोग गर्ने अनुमति दिन्छ।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"जुम नियन्त्रणको लागि दुई चोटि ट्याप गर्नुहोस्"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट थप गर्न सकिँदैन।"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"जानुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3b6db06..686c951 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Verwijderen"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Invoermethode"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tekstacties"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Opslagruimte is bijna vol"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bepaalde systeemfuncties werken mogelijk niet"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Hiermee kan een app rechten vragen om batterijoptimalisatie voor die app te negeren."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"alle pakketten opvragen"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Hiermee kan een app alle geïnstalleerde pakketten zien."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"toegang tot SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Geeft een app toegang tot SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tik twee keer voor zoomregeling"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kan widget niet toevoegen."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ga"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0220ce3..69ca39c 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ଡିଲିଟ୍‍ କରନ୍ତୁ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ଇନପୁଟ୍ ପଦ୍ଧତି"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ଟେକ୍ସଟ୍‌ କାର୍ଯ୍ୟ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ଷ୍ଟୋରେଜ୍‌ ସ୍ପେସ୍‌ ଶେଷ ହେବାରେ ଲାଗିଛି"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍‌ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ଆପ୍‍ ପାଇଁ ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବାର ଅନୁମତି ମାଗିବା ନିମନ୍ତେ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ସବୁ ପ୍ୟାକେଜ୍ ବିଷୟରେ କ୍ୱେରୀ କରନ୍ତୁ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ସମସ୍ତ ପ୍ୟାକେଜକୁ ଦେଖିବା ପାଇଁ ଏକ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApiଗୁଡ଼ିକୁ ଆକ୍ସେସ କରନ୍ତୁ"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApiଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏକ ଆପ୍ଲିକେସନକୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍‌ କରନ୍ତୁ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ୱିଜେଟ୍‍ ଯୋଡ଼ିପାରିବ ନାହିଁ।"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ଯାଆନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7a3bc33..864dda2 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ਮਿਟਾਓ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ਇਨਪੁੱਟ ਵਿਧੀ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ਟੈਕਸਟ ਕਿਰਿਆਵਾਂ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ਕਿਸੇ ਐਪ ਨੂੰ ਉਸ ਵਾਸਤੇ ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਇਜਾਜ਼ਤ ਵਾਸਤੇ ਪੁੱਛਣ ਲਈ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ਸਾਰੇ ਪੈਕੇਜਾਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਸਥਾਪਤ ਕੀਤੇ ਸਾਰੇ ਪੈਕੇਜ ਦੇਖਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ।"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis ਤੱਕ ਪਹੁੰਚ"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"ਕਿਸੇ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ SupplementalApis ਤੱਕ ਪਹੁੰਚ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ਜ਼ੂਮ ਕੰਟਰੋਲ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋ ਸਕਿਆ।"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ਜਾਓ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index bbc1867..4d9a07c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Usuń"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Sposób wprowadzania tekstu"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Działania na tekście"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kończy się miejsce"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektóre funkcje systemu mogą nie działać"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Zezwala aplikacji na proszenie o uprawnienia do ignorowania optymalizacji wykorzystania baterii w przypadku danej aplikacji."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zapytanie o wszystkie pakiety"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Pozwala aplikacji wyświetlać wszystkie zainstalowane pakiety."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"dostęp do SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Zezwala na dostęp aplikacji do SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dotknij dwukrotnie, aby sterować powiększeniem"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nie można dodać widżetu."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 3835310..7b7fd55 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Excluir"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Ações de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acessar SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que um aplicativo tenha acesso a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c2df19a..b4c191d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acções de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Está quase sem espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema poderão não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que uma app solicite autorização para ignorar as otimizações da bateria para a mesma."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite a uma app ver todos os pacotes instalados."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"aceder a SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite a uma aplicação aceder a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocar duas vezes para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3835310..7b7fd55 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Excluir"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Ações de texto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"acessar SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite que um aplicativo tenha acesso a SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4469a67..ffa84e3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Ștergeți"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metodă de intrare"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acțiuni pentru text"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spațiul de stocare aproape ocupat"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"să acceseze SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Permite unei aplicații să acceseze SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6b874b9..2993b48 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Удалить"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Способ ввода"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Операции с текстом"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Недостаточно памяти"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некоторые функции могут не работать"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешает приложению игнорировать ограничение на расход заряда батареи."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Запрос информации обо всех пакетах"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Приложение сможет просматривать все установленные пакеты."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Доступ к SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Приложение сможет получать доступ к SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Нажмите дважды для изменения масштаба"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не удалось добавить виджет."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Выбрать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 3443681..f243dab 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"මකන්න"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ආදාන ක්‍රමය"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"පෙළ ක්‍රියාවන්"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ආචයනය ඉඩ ප්‍රමාණය අඩු වී ඇත"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"සමහර පද්ධති කාර්යයන් ක්‍රියා නොකරනු ඇත"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"පද්ධතිය සඳහා ප්‍රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"යෙදුමකට එම යෙදුම සඳහා බැටරි ප්‍රශස්තකරණ නොසලකා හැරීමට අවසර ඉල්ලීමට ඉඩ දෙයි."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"සියලු පැකේජ විමසන්න"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ස්ථාපනය කර ඇති සියලු පැකේජ බැලීමට යෙදුමකට ඉඩ දෙයි."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis වෙත ප්‍රවේශ වන්න"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApis වෙත ප්‍රවේශ වීමට යෙදුමකට ඉඩ දෙයි."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"විශාලන පාලක සඳහා දෙවතාවක් තට්ටු කරන්න"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"විජටය එකතු කිරීමට නොහැකි විය."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"යන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 517ce6c..f2b3ae1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Odstrániť"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metóda vstupu"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Operácie s textom"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nedostatok ukladacieho priestoru"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektoré systémové funkcie nemusia fungovať"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Umožňuje aplikácii požiadať o povolenie ignorovať optimalizácie výdrže batérie pre danú aplikáciu."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"dopytovať všetky balíky"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Povoľuje aplikácii čítať všetky nainštalované balíky."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"prístup k rozhraniam SupplementalApi"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Umožňuje aplikácii získať prístup k rozhraniam SupplementalApi."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvojitým klepnutím môžete ovládať priblíženie"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Miniaplikáciu sa nepodarilo pridať."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Hľadať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7bcc257..0d71d3b 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Način vnosa"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Besedilna dejanja"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Prostor za shranjevanje bo pošel"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nekatere sistemske funkcije morda ne delujejo"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji dovoljuje, da vpraša za dovoljenje, ali naj prezre optimizacije baterije."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"poizvedovanje po vseh paketih"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji dovoli, da vidi vse nameščene pakete."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"dostop do SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Aplikaciji omogoča dostop do SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapnite dvakrat za nadzor povečave/pomanjšave"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Pripomočka ni bilo mogoče dodati."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Pojdi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 0737100..3050c84 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Fshi"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metoda e hyrjes"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Veprimet e tekstit"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Hapësira ruajtëse po mbaron"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Disa funksione të sistemit mund të mos punojnë"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lejon që një aplikacion të kërkojë leje për të shpërfillur optimizimet e baterisë për atë aplikacion."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kërko të gjitha paketat"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lejon një aplikacion të shikojë të gjitha paketat e instaluara."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"qasje te SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Lejon një aplikacion të ketë qasje në SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trokit dy herë për të kontrolluar zmadhimin"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nuk mundi të shtonte miniaplikacion."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Shko"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index e117bda..c66bb8c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1207,6 +1207,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метод уноса"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Радње у вези са текстом"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Меморијски простор је на измаку"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Неке системске функције можда не функционишу"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1511,6 +1515,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дозвољава апликацији да тражи дозволу за игнорисање оптимизација батерије за ту апликацију."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"слање упита за све пакете"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозвољава апликацији да види све инсталиране пакете."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"приступ ставци SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Дозвољава апликацији да приступа ставци SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Додирните двапут за контролу зумирања"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Није могуће додати виџет."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Иди"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 90de245..bb40f87 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Ta bort"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Indatametod"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Textåtgärder"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lagringsutrymmet börjar ta slut"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Appen får be om tillstånd att ignorera batterioptimering."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"fråga alla paket"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tillåter att en app ser alla installerade paket."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"åtkomst till SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Tillåter att en app får åtkomst till SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Peka två gånger för zoomkontroll"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Det gick inte att lägga till widgeten."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Kör"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ec24102..58e73ca 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Futa"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Mbinu ya uingizaji"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Vitendo vya maandishi"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kutuma hoja kwa vifurushi vyote"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Huruhusu programu kuona vifurushi vyote vilivyosakinishwa."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"kufikia SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Huruhusu programu kufikia SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Gusa mara mbili kwa udhibiti wa kuza"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Haikuweza kuongeza wijeti."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Nenda"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index e7ea59d..f4f37a6 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"நீக்கு"</string>
     <string name="inputMethod" msgid="1784759500516314751">"உள்ளீட்டு முறை"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"உரை நடவடிக்கைகள்"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"சேமிப்பிடம் குறைகிறது"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"பயன்பாட்டிற்கான பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோர, ஆப்ஸை அனுமதிக்கும்."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"அனைத்துப் பேக்கேஜ்களையும் பார்க்க அனுமதித்தல்"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"நிறுவப்பட்டுள்ள அனைத்துப் பேக்கேஜ்களையும் பார்ப்பதற்கு ஆப்ஸை அனுமதிக்கும்."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApiகளை அணுகுதல்"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApiகளை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"அளவை மாற்றுவதற்கான கட்டுப்பாட்டிற்கு, இருமுறை தட்டவும்"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"செல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 176d845..46fe375 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -174,7 +174,7 @@
     <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ రిక్వెస్ట్‌లు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
     <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g>కు సైన్‌ఇన్ ఎర్రర్"</string>
     <string name="contentServiceSync" msgid="2341041749565687871">"సింక్‌"</string>
-    <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సమకాలీకరించడం సాధ్యపడదు"</string>
+    <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సింక్ చేయడం సాధ్యపడదు"</string>
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"చాలా ఎక్కువ <xliff:g id="CONTENT_TYPE">%s</xliff:g> తొలగించడానికి ప్రయత్నించారు."</string>
     <string name="low_memory" product="tablet" msgid="5557552311566179924">"టాబ్లెట్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
     <string name="low_memory" product="watch" msgid="3479447988234030194">"వాచ్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
@@ -224,7 +224,7 @@
     <string name="silent_mode_vibrate" msgid="8821830448369552678">"రింగర్ వైబ్రేట్‌లో ఉంది"</string>
     <string name="silent_mode_ring" msgid="6039011004781526678">"రింగర్ ఆన్‌లో ఉంది"</string>
     <string name="reboot_to_update_title" msgid="2125818841916373708">"Android సిస్టమ్ అప్‌డేట్"</string>
-    <string name="reboot_to_update_prepare" msgid="6978842143587422365">"నవీకరించడానికి సిద్ధం చేస్తోంది…"</string>
+    <string name="reboot_to_update_prepare" msgid="6978842143587422365">"అప్‌డేట్ చేయడానికి సిద్ధం చేస్తోంది…"</string>
     <string name="reboot_to_update_package" msgid="4644104795527534811">"అప్‌డేట్ ప్యాకేజీని ప్రాసెస్ చేస్తోంది…"</string>
     <string name="reboot_to_update_reboot" msgid="4474726009984452312">"పునఃప్రారంభించబడుతోంది…"</string>
     <string name="reboot_to_reset_title" msgid="2226229680017882787">"ఫ్యాక్టరీ డేటా రీసెట్ చేయండి"</string>
@@ -369,7 +369,7 @@
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్‌లను చదవడం"</string>
     <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్‌లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్‌ను స్వీకరించినప్పుడు హానికరమైన యాప్‌లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్‌లను చదవడం"</string>
-    <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్‌ల గురించి వివరాలను పొందడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సింక్ చేసిన ఫీడ్‌ల గురించి వివరాలను పొందడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్‌లను పంపడం, వీక్షించడం"</string>
     <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్‌లు పంపడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే మెసేజ్‌లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
     <string name="permlab_readSms" msgid="5164176626258800297">"మీ టెక్స్ట్ మెసేజ్‌లు (SMS లేదా MMS) చదవడం"</string>
@@ -403,7 +403,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"యాప్ నిల్వ స్థలాన్ని అంచనా వేయడం"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"యాప్‌ కోడ్, డేటా మరియు కాష్ పరిమాణాలను తిరిగి పొందడానికి దాన్ని అనుమతిస్తుంది"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"సిస్టమ్ సెట్టింగ్‌లను మార్చడం"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"సిస్టమ్ యొక్క సెట్టింగ్‌ల డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ సిస్టమ్ యొక్క కాన్ఫిగరేషన్‌ను నాశనం చేయవచ్చు."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"ప్రారంభంలో అమలు చేయడం"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే దానికదే ప్రారంభించబడటానికి యాప్‌ను అనుమతిస్తుంది. ఇది టాబ్లెట్‌ను ప్రారంభించడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు చేయడం ద్వారా మొత్తం టాబ్లెట్‌ను నెమ్మదిగా పని చేయడానికి యాప్‌ను అనుమతించేలా చేయవచ్చు."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"సిస్టమ్ బూటింగ్‌ను పూర్తి చేసిన వెంటనే యాప్ దానికదే ప్రారంభం కావడానికి అనుమతిస్తుంది. ఇది మీ Android TV పరికరం ప్రారంభం కావడానికి ఎక్కువ సమయం పట్టేలా చేయవచ్చు మరియు ఎల్లప్పుడూ అమలు కావడం ద్వారా మొత్తం పరికరం పనితీరును నెమ్మది చేయడానికి యాప్‌ను అనుమతించవచ్చు."</string>
@@ -417,15 +417,15 @@
     <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"మీ Android TV పరికరంలో నిల్వ చేసిన కాంటాక్ట్‌లకు సంబంధించిన డేటాను చదవడానికి యాప్‌ను అనుమతిస్తుంది. కాంటాక్ట్‌లను సృష్టించిన మీ Android TV పరికరంలోని ఖాతాలకు కూడా యాప్‌లకు యాక్సెస్ ఉంటుంది. ఇందులో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఉండవచ్చు. ఈ అనుమతి, మీ కాంటాక్ట్ డేటాను సేవ్ చేయడానికి యాప్‌లను అనుమతిస్తుంది, హానికరమైన యాప్‌లు మీకు తెలియకుండానే కాంటాక్ట్ డేటాను షేర్ చేయవచ్చు."</string>
     <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"ఫోన్‌లో నిల్వ చేసిన మీ కాంటాక్ట్‌లకు సంబంధించిన డేటాను చదవడానికి యాప్‌ను అనుమతిస్తుంది. కాంటాక్ట్‌లను సృష్టించిన మీ ఫోన్‌లోని ఖాతాలను కూడా యాప్‌లు యాక్సెస్ చేయగలవు. ఇందులో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఉండవచ్చు. ఈ అనుమతి, మీ కాంటాక్ట్ డేటాను సేవ్ చేయడానికి యాప్‌లను అనుమతిస్తుంది, హానికరమైన యాప్‌లు మీకు తెలియకుండానే కాంటాక్ట్ డేటాను షేర్ చేయవచ్చు."</string>
     <string name="permlab_writeContacts" msgid="8919430536404830430">"మీ కాంటాక్ట్‌లను సవరించడం"</string>
-    <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"మీ టాబ్లెట్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
-    <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"మీ Android TV పరికరంలో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
-    <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+    <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"మీ టాబ్లెట్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+    <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"మీ Android TV పరికరంలో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
+    <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
     <string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్‌ను చదవడం"</string>
     <string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్‌ మీ కాల్ చరిత్రను చదవగలదు."</string>
     <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను రాయడం"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"శరీర సెన్సార్‌లను (గుండె స్పందన రేటు మానిటర్‌ల వంటివి) యాక్సెస్ చేయండి"</string>
     <string name="permdesc_bodySensors" product="default" msgid="3208940894182188063">"గుండె స్పందన రేటు, ఉష్ణోగ్రత, రక్తంలో ఆక్సిజన్ శాతం మొదలైన శరీర సెన్సార్‌ల నుండి డేటాను యాక్సెస్ చేయండి."</string>
     <string name="permlab_bodySensors_background" msgid="4352831883331744370">"బ్యాక్‌గ్రౌండ్‌లో శరీర సెన్సార్‌లను (గుండె రేటు మానిటర్స్) యాక్సెస్ చేయండి"</string>
@@ -447,7 +447,7 @@
     <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"బ్యాక్‌గ్రౌండ్‌లో లొకేషన్‌ను యాక్సెస్ చేయి"</string>
     <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"యాప్ ఉపయోగంలో లేనప్పటికీ కూడా, ఈ యాప్, లొకేషన్‌ను ఎప్పుడైనా యాక్సెస్ చేయగలదు."</string>
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"మీ ఆడియో సెట్టింగ్‌లను మార్చడం"</string>
-    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"వాల్యూమ్ మరియు అవుట్‌పుట్ కోసం ఉపయోగించాల్సిన స్పీకర్ వంటి సార్వజనీన ఆడియో సెట్టింగ్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"ఆడియోను రికార్డ్ చేయడం"</string>
     <string name="permdesc_recordAudio" msgid="5857246765327514062">"యాప్ ఉపయోగంలో ఉన్నపుడు మైక్రోఫోన్‌ను ఉపయోగించి ఈ యాప్, ఆడియోను రికార్డ్ చేయగలదు."</string>
     <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"బ్యాక్‌గ్రౌండ్‌లో ఆడియోను రికార్డ్ చేయగలదు"</string>
@@ -566,11 +566,11 @@
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"వేలిముద్ర హార్డ్‌వేర్‌ని ఉపయోగించడానికి అనుమతి"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను ఎడిట్ చేయండి"</string>
-    <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను ఎడిట్ చేయండి"</string>
-    <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను సవరించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది."</string>
     <string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను ఎడిట్ చేయండి"</string>
-    <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_mediaLocation" msgid="7368098373378598066">"మీ మీడియా సేకరణ నుండి లొకేషన్లను చదవండి"</string>
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి లొకేషన్లను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"బయోమెట్రిక్స్‌ను ఉపయోగించండి"</string>
@@ -678,7 +678,7 @@
     <string name="permlab_readSyncSettings" msgid="6250532864893156277">"సింక్ సెట్టింగ్‌లను చదవగలగడం"</string>
     <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్‌ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
     <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"\'సింక్\'ను ఆన్, ఆఫ్‌ల మధ్య టోగుల్ చేయడం"</string>
-    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్‌ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
+    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్‌ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"సింక్ గణాంకాలను చదవగలగడం"</string>
     <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సింక్‌ గణాంకాలను అలాగే సింక్‌ ఈవెంట్‌ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_sdcardRead" msgid="5791467020950064920">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను చదువుతుంది"</string>
@@ -704,7 +704,7 @@
     <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"నెట్‌వర్క్ విధానాన్ని నిర్వహించడం"</string>
     <string name="permdesc_manageNetworkPolicy" msgid="1865663268764673296">"నెట్‌వర్క్ విధానాలను నిర్వహించడానికి మరియు యాప్-నిర్దిష్ట నిబంధనలను నిర్వచించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"నెట్‌వర్క్ వినియోగ అకౌంటింగ్‌ను సవరించడం"</string>
-    <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
+    <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
     <string name="permlab_accessNotifications" msgid="7130360248191984741">"నోటిఫికేషన్‌లను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర యాప్‌ల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"నోటిఫికేషన్ పరిశీలన సేవకు అనుబంధించడం"</string>
@@ -718,7 +718,7 @@
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడం"</string>
     <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"ఇన్‌పుట్ పరికరం క్రమాంకనాన్ని మార్చండి"</string>
-    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM ప్రమాణపత్రాలను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam బదిలీ స్టేటస్‌ని స్వీకరించడం"</string>
@@ -1029,14 +1029,14 @@
     <string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"బ్రౌజర్ సందర్శించిన అన్ని URLల చరిత్ర గురించి మరియు అన్ని బ్రౌజర్ బుక్‌మార్క్‌ల గురించి చదవడానికి యాప్‌ను అనుమతిస్తుంది. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను రాయడం"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"మీ టాబ్లెట్‌లో నిల్వ చేయబడిన బ్రౌజర్ హిస్టరీని, బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతిని థర్డ్ పార్టీ బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌లు అమలు చేయకపోవచ్చు."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా సవరించడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
     <string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string>
     <string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్‌స్టాల్ చేయబడిన అలారం గడియారం యాప్‌లో అలారంను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్‌లు ఈ ఫీచర్‌ను అమలు చేయకపోవచ్చు."</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్‌ను జోడించడం"</string>
     <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్‌బాక్స్‌కు మెసేజ్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు ఏకపక్ష వెబ్ సైట్‌లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు ఏకపక్ష వెబ్ సైట్‌లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్‌వర్డ్‌ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
     <string name="save_password_notnow" msgid="2878327088951240061">"ఇప్పుడు కాదు"</string>
     <string name="save_password_remember" msgid="6490888932657708341">"గుర్తుంచుకో"</string>
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"తొలగించు"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ఇన్‌పుట్ పద్ధతి"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"వచనానికి సంబంధించిన చర్యలు"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"నిల్వ ఖాళీ అయిపోతోంది"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"అన్ని ప్యాకేజీలను క్వెరీ చేయండి"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ఇన్‌స్టాల్ చేసిన అన్ని ప్యాకేజీలను చూడటానికి యాప్‌ను అనుమతించండి."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApisని యాక్సెస్ చేయండి"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"SupplementalApisని యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string>
@@ -1871,7 +1877,7 @@
     <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"ప్రస్తుత పిన్‌"</string>
     <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"కొత్త పిన్‌"</string>
     <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"కొత్త పిన్‌ను నిర్ధారించండి"</string>
-    <string name="restr_pin_create_pin" msgid="917067613896366033">"నియంత్రణలను సవరించడానికి పిన్‌ను రూపొందించండి"</string>
+    <string name="restr_pin_create_pin" msgid="917067613896366033">"నియంత్రణలను ఎడిట్ చేయడానికి పిన్‌ను రూపొందించండి"</string>
     <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"పిన్‌లు సరిపోలలేదు. మళ్లీ ప్రయత్నించండి."</string>
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"పిన్‌ చాలా చిన్నదిగా ఉంది. తప్పనిసరిగా కనీసం 4 అంకెలు ఉండాలి."</string>
     <plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
@@ -1879,7 +1885,7 @@
       <item quantity="one">1 సెకనులో మళ్లీ ప్రయత్నించండి</item>
     </plurals>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"తర్వాత మళ్లీ ప్రయత్నించండి"</string>
-    <string name="immersive_cling_title" msgid="2307034298721541791">"పూర్తి స్క్రీన్‌లో వీక్షిస్తున్నారు"</string>
+    <string name="immersive_cling_title" msgid="2307034298721541791">"ఫుల్-స్క్రీన్‌లో వీక్షిస్తున్నారు"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"నిష్క్రమించడానికి, పై నుండి క్రిందికి స్వైప్ చేయండి."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"అర్థమైంది"</string>
     <string name="done_label" msgid="7283767013231718521">"పూర్తయింది"</string>
@@ -2148,8 +2154,8 @@
     <string name="mime_type_image_ext" msgid="5743552697560999471">"<xliff:g id="EXTENSION">%1$s</xliff:g> చిత్రం"</string>
     <string name="mime_type_compressed" msgid="8737300936080662063">"ఆర్కైవ్"</string>
     <string name="mime_type_compressed_ext" msgid="4775627287994475737">"<xliff:g id="EXTENSION">%1$s</xliff:g> ఆర్కైవ్"</string>
-    <string name="mime_type_document" msgid="3737256839487088554">"పత్రం"</string>
-    <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> పత్రం"</string>
+    <string name="mime_type_document" msgid="3737256839487088554">"డాక్యుమెంట్‌"</string>
+    <string name="mime_type_document_ext" msgid="2398002765046677311">"<xliff:g id="EXTENSION">%1$s</xliff:g> డాక్యుమెంట్‌"</string>
     <string name="mime_type_spreadsheet" msgid="8188407519131275838">"స్ప్రెడ్‌షీట్"</string>
     <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> స్ప్రెడ్‌షీట్"</string>
     <string name="mime_type_presentation" msgid="1145384236788242075">"ప్రదర్శన"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 30b3d0b..f485e85 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"ลบ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"วิธีป้อนข้อมูล"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"การทำงานของข้อความ"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"พื้นที่จัดเก็บเหลือน้อย"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"อนุญาตให้แอปขอสิทธิ์เพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับแอปนั้น"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ค้นหาแพ็กเกจทั้งหมด"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"อนุญาตให้แอปดูแพ็กเกจที่ติดตั้งไว้ทั้งหมด"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"เข้าถึง SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"อนุญาตให้แอปพลิเคชันเข้าถึง SupplementalApis"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"ไป"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c0bbc69..6448755 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"I-delete"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Pamamaraan ng pag-input"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Pagkilos ng teksto"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nauubusan na ang puwang ng storage"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Pinapayagang humingi ng pahintulot ang isang app na balewalain ang mga pag-optimize ng baterya para sa app na iyon."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"i-query ang lahat ng package"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Nagbibigay-daan sa isang app na makita ang lahat ng naka-install na package."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"access sa SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Nagbibigay-daan sa isang application na i-access ang SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapikin ng dalawang beses para sa pagkontrol ng zoom"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Hindi maidagdag ang widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Pumunta"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8a86227..e3c20baf 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Sil"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Giriş yöntemi"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Metin eylemleri"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Depolama alanı bitiyor"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bazı sistem işlevleri çalışmayabilir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bir uygulamanın, kendisi için pil optimizasyonlarını göz ardı etme izni istemesine olanak sağlar."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"tüm paketleri sorgulama"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Uygulamaya tüm yüklü paketleri görme izni verir."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis\'e erişim"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Bir uygulamanın SupplementalApis\'e erişimine izin verir."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zum denetimi için iki kez dokun"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget eklenemedi."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Git"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f4fb575..c920e64 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1227,6 +1227,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Видалити"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метод введення"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Дії з текстом"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Закінчується пам’ять"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Деякі системні функції можуть не працювати"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1531,6 +1535,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Додаток зможе запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"подавати запити на всі пакети"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволяє додатку переглядати всі встановлені пакети."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"Доступ до SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Надає додатку доступ до SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Двічі натис. для кер. масшт."</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не вдалося додати віджет."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Йти"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2c712f4..17da4e4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"حذف کریں"</string>
     <string name="inputMethod" msgid="1784759500516314751">"اندراج کا طریقہ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"متن کی کارروائیاں"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"اس ایپ کیلئے ایک ایپ کو بیٹری کی کارکردگی  بہتر بنانے کو نظر انداز کرنے کی اجازت دیں۔"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"سبھی پیکیجز سے متعلق استفسار کریں"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ایپ کو سبھی انسٹال کردہ پیکیجز دیکھنے کی اجازت دیتا ہے۔"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"‏SupplementalApis تک رسائی حاصل کریں"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"‏ایپلیکیشن کو SupplementalApis تک رسائی حاصل کرنے کی اجازت دیتا ہے۔"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"زوم کنٹرول کیلئے دوبار تھپتھپائیں"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"ویجٹس کو شامل نہیں کرسکا۔"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"جائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 42a88d7..880a22d 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"O‘chirish"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Kiritish uslubi"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Matn yozish"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Xotirada joy yetarli emas"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ayrim funksiyalar ishlamasligi mumkin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ilovaga batareya quvvatidan xohlagancha foydalanish uchun ruxsat so‘rashga imkon beradi."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"barcha paketlarni chiqarish"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ilova oʻrnatilgan barcha paketlarni koʻrishiga ruxsat beradi"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"SupplementalApis omboriga ruxsat"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ilovaga SupplementalApis omboriga ruxsat beradi."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidjet qo‘shilmadi."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Tanlash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 05899ed..448207e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Xóa"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Phương thức nhập"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Tác vụ văn bản"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Sắp hết dung lượng lưu trữ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Một số chức năng hệ thống có thể không hoạt động"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Cho phép ứng dụng hỏi quyền để bỏ qua tối ưu hóa pin cho ứng dụng đó."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"truy vấn tất cả các gói"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Cho phép một ứng dụng xem tất cả các gói đã cài đặt."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"quyền truy cập vào SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Cho phép ứng dụng truy cập vào SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Nhấn hai lần để kiểm soát thu phóng"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Không thể thêm tiện ích."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Đến"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6acd3e0..6c330f2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"删除"</string>
     <string name="inputMethod" msgid="1784759500516314751">"输入法"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"文字操作"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"存储空间不足"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"某些系统功能可能无法正常使用"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允许应用请求相应的权限,以便忽略针对该应用的电池优化。"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查询所有软件包"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允许应用查看所有已安装的软件包。"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"访问 SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允许应用访问 SupplementalApis。"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"双击可以进行缩放控制"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"无法添加微件。"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"开始"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bf51c1a..7a468f7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"刪除"</string>
     <string name="inputMethod" msgid="1784759500516314751">"輸入法"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"文字操作"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求就該應用程式忽略電池優化。"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"存取 SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允許應用程式存取 SupplementalApis。"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"輕觸兩下控制縮放"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index bd0b9a2..e570d65 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"刪除"</string>
     <string name="inputMethod" msgid="1784759500516314751">"輸入法"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"文字動作"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求權限,以便忽略針對該應用程式的電池效能最佳化設定。"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"存取 SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"允許應用程式存取 SupplementalApis。"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"點兩下以進行縮放控制"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index bf024528..301dced 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1187,6 +1187,10 @@
     <string name="deleteText" msgid="4200807474529938112">"Susa"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Indlela yokufakwayo"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Izenzo zombhalo"</string>
+    <!-- no translation found for input_method_nav_back_button_desc (3655838793765691787) -->
+    <skip />
+    <!-- no translation found for input_method_ime_switch_button_desc (2736542240252198501) -->
+    <skip />
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Isikhala sokulondoloza siyaphela"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1491,6 +1495,8 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ivumela uhlelo lokusebenza ukuthi licele imvume yokuziba ukulungiselela ibhethri yalolo hlelo lokusebenza."</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buza wonke amaphakheji"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ivumela i-app ibone wonke amaphakheji afakiwe."</string>
+    <string name="permlab_accessSupplementalApi" msgid="3544659160536960275">"finyelela i-SupplementalApis"</string>
+    <string name="permdesc_accessSupplementalApi" msgid="8974758769370951074">"Ivumela i-application ukuba ifinyelele i-SupplementalApis."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Thepha kabili ukuthola ukulawula ukusondeza"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Yehlulekile ukwengeza i-widget."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"Iya"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2dd17cf..78841fc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2373,6 +2373,9 @@
     <bool name="config_dreamsActivatedOnSleepByDefault">false</bool>
     <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
     <string name="config_dreamsDefaultComponent" translatable="false">com.android.deskclock/com.android.deskclock.Screensaver</string>
+    <!-- ComponentNames of the dreams that we should hide -->
+    <string-array name="config_disabledDreamComponents" translatable="false">
+    </string-array>
 
     <!-- Are we allowed to dream while not plugged in? -->
     <bool name="config_dreamsEnabledOnBattery">false</bool>
@@ -5621,4 +5624,14 @@
 
     <!-- Whether or not to enable the lock screen entry point for the QR code scanner. -->
     <bool name="config_enableQrCodeScannerOnLockScreen">false</bool>
+
+    <!-- Whether Low Power Standby is supported and can be enabled. -->
+    <bool name="config_lowPowerStandbySupported">false</bool>
+
+    <!-- If supported, whether Low Power Standby is enabled by default. -->
+    <bool name="config_lowPowerStandbyEnabledByDefault">false</bool>
+
+    <!-- The amount of time after becoming non-interactive (in ms) after which
+         Low Power Standby can activate. -->
+    <integer name="config_lowPowerStandbyNonInteractiveTimeout">5000</integer>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index d374b74..4874e65 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -131,15 +131,15 @@
         corners. -->
     <dimen name="rounded_corner_radius_bottom_adjustment">0px</dimen>
 
+    <!-- Default paddings for content around the corners. -->
+    <dimen name="rounded_corner_content_padding">0dp</dimen>
+
     <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME. -->
     <dimen name="input_method_navigation_key_width">70dp</dimen>
     <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME. -->
     <dimen name="input_method_navigation_key_padding">0dp</dimen>
     <!-- Copied from SysUI's @dimen/nav_content_padding for the embedded nav bar in the IME. -->
     <dimen name="input_method_nav_content_padding">0px</dimen>
-    <!-- Copied from SysUI's @dimen/rounded_corner_content_padding for the embedded nav bar in the
-         IME. -->
-    <dimen name="input_method_rounded_corner_content_padding">0px</dimen>
     <!-- Copied from SysUI's @dimen/key_button_ripple_max_width for the embedded nav bar in the
          IME. -->
     <dimen name="input_method_nav_key_button_ripple_max_width">95dp</dimen>
@@ -877,6 +877,9 @@
     <!-- Maximum number of datasets that are visible in the UX picker without scrolling -->
     <integer name="autofill_max_visible_datasets">3</integer>
 
+    <!-- Size of an icon in the Autolfill fill dialog -->
+    <dimen name="autofill_dialog_icon_size">56dp</dimen>
+
     <!-- Size of a slice shortcut view -->
     <dimen name="slice_shortcut_size">56dp</dimen>
     <!-- Size of action icons in a slice -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index db348ed..bccd2b6 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -59,6 +59,12 @@
   <item type="id" name="candidatesArea" />
   <item type="id" name="inputArea" />
   <item type="id" name="inputExtractEditText" />
+  <!-- View id for the action of text editor inside of an extracted text
+        {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+  <item type="id" name="inputExtractAction" />
+  <!-- View id for the accessories of text editor inside of an extracted text
+        {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+  <item type="id" name="inputExtractAccessories" />
   <item type="id" name="selectAll" />
   <item type="id" name="cut" />
   <item type="id" name="copy" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 655deac..2e96c65 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3268,6 +3268,8 @@
     <public name="accessibilityActionSwipeUp" />
     <public name="accessibilityActionSwipeDown" />
     <public name="accessibilityActionShowSuggestions" />
+    <public name="inputExtractAction" />
+    <public name="inputExtractAccessories" />
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x01dd0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 44ede49..49a12d1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3872,6 +3872,11 @@
     <!-- Message of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
     <string name="console_running_notification_message">Performance is impacted. To disable, check bootloader.</string>
 
+    <!-- Title of notification shown when MTE status override is enabled. [CHAR LIMIT=NONE] -->
+    <string name="mte_override_notification_title">Experimental MTE enabled</string>
+    <!-- Message of notification shown when MTE status override is enabled. [CHAR LIMIT=NONE] -->
+    <string name="mte_override_notification_message">Performance and stability might be impacted. Reboot to disable. If enabled using arm64.memtag.bootctl, set it to "none" beforehand.</string>
+
     <!-- Title of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
     <string name="usb_contaminant_detected_title">Liquid or debris in USB port</string>
     <!-- Message of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
@@ -5459,6 +5464,15 @@
         <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
     </string>
 
+    <!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+    <string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string>
+    <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+    <string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string>
+    <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+    <string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string>
+    <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+    <string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string>
+
     <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
     <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
     <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 67369d2..20c5c61 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2080,6 +2080,8 @@
   <java-symbol type="string" name="test_harness_mode_notification_message" />
   <java-symbol type="string" name="console_running_notification_title" />
   <java-symbol type="string" name="console_running_notification_message" />
+  <java-symbol type="string" name="mte_override_notification_title" />
+  <java-symbol type="string" name="mte_override_notification_message" />
   <java-symbol type="string" name="taking_remote_bugreport_notification_title" />
   <java-symbol type="string" name="share_remote_bugreport_notification_title" />
   <java-symbol type="string" name="sharing_remote_bugreport_notification_title" />
@@ -2211,6 +2213,7 @@
   <java-symbol type="integer" name="config_dreamsBatteryLevelDrainCutoff" />
   <java-symbol type="string" name="config_dreamsDefaultComponent" />
   <java-symbol type="drawable" name="default_dream_preview" />
+  <java-symbol type="array" name="config_disabledDreamComponents" />
   <java-symbol type="string" name="config_dozeComponent" />
   <java-symbol type="string" name="enable_explore_by_touch_warning_title" />
   <java-symbol type="string" name="enable_explore_by_touch_warning_message" />
@@ -3263,6 +3266,9 @@
   <java-symbol type="string" name="app_blocked_title" />
   <java-symbol type="string" name="app_blocked_message" />
 
+  <java-symbol type="string" name="app_streaming_blocked_title" />
+  <java-symbol type="string" name="app_streaming_blocked_message" />
+
   <!-- Used internally for assistant to launch activity transitions -->
   <java-symbol type="id" name="cross_task_transition" />
 
@@ -3504,6 +3510,7 @@
   <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
   <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
+  <java-symbol type="layout" name="autofill_fill_dialog"/>
   <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_dataset_footer"/>
   <java-symbol type="id" name="autofill_dataset_header"/>
@@ -3516,6 +3523,13 @@
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_title" />
   <java-symbol type="id" name="autofill_save_yes" />
+  <java-symbol type="id" name="autofill_service_icon" />
+  <java-symbol type="id" name="autofill_dialog_picker"/>
+  <java-symbol type="id" name="autofill_dialog_header"/>
+  <java-symbol type="id" name="autofill_dialog_container"/>
+  <java-symbol type="id" name="autofill_dialog_list"/>
+  <java-symbol type="id" name="autofill_dialog_no" />
+  <java-symbol type="id" name="autofill_dialog_yes" />
   <java-symbol type="string" name="autofill_error_cannot_autofill" />
   <java-symbol type="string" name="autofill_picker_no_suggestions" />
   <java-symbol type="string" name="autofill_picker_some_suggestions" />
@@ -4666,4 +4680,8 @@
   <java-symbol type="string" name="notification_content_abusive_bg_apps"/>
   <java-symbol type="string" name="notification_content_long_running_fgs"/>
   <java-symbol type="string" name="notification_action_check_bg_apps"/>
+
+  <java-symbol type="bool" name="config_lowPowerStandbySupported" />
+  <java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" />
+  <java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 205c517..c88e512 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -154,9 +154,9 @@
     <!-- Israel: 4 digits, known premium codes listed -->
     <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
 
-    <!-- Italy: 5 digits (premium=4xxxx), plus EU:
-         http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
-    <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503" standard="43\\d{3}" />
+    <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
+         https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
+    <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" />
 
     <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
     <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 75da0bf..1467fed 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -29,7 +29,6 @@
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.util.MergedConfiguration;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
@@ -110,7 +109,6 @@
         private ProfilerInfo mProfilerInfo;
         private IBinder mAssistToken;
         private IBinder mShareableActivityToken;
-        private FixedRotationAdjustments mFixedRotationAdjustments;
         private boolean mLaunchedFromBubble;
 
         LaunchActivityItemBuilder setIntent(Intent intent) {
@@ -203,11 +201,6 @@
             return this;
         }
 
-        LaunchActivityItemBuilder setFixedRotationAdjustments(FixedRotationAdjustments fra) {
-            mFixedRotationAdjustments = fra;
-            return this;
-        }
-
         LaunchActivityItemBuilder setLaunchedFromBubble(boolean launchedFromBubble) {
             mLaunchedFromBubble = launchedFromBubble;
             return this;
@@ -218,8 +211,8 @@
                     mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
                     mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
-                    null /* activityClientController */, mFixedRotationAdjustments,
-                    mShareableActivityToken, mLaunchedFromBubble);
+                    null /* activityClientController */, mShareableActivityToken,
+                    mLaunchedFromBubble);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 60d48b2..b2c4274 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -56,9 +56,6 @@
 import android.os.RemoteException;
 import android.os.SharedMemory;
 import android.platform.test.annotations.Presubmit;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-import android.view.DisplayCutout;
-import android.view.Surface;
 import android.view.autofill.AutofillId;
 import android.view.translation.TranslationSpec;
 import android.view.translation.UiTranslationSpec;
@@ -198,8 +195,6 @@
         bundle.putParcelable("data", new ParcelableData(1));
         PersistableBundle persistableBundle = new PersistableBundle();
         persistableBundle.putInt("k", 4);
-        FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments(
-                Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT);
 
         LaunchActivityItem item = new LaunchActivityItemBuilder()
                 .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
@@ -207,8 +202,7 @@
                 .setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
                 .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic())
                 .setPendingNewIntents(referrerIntentList()).setIsForward(true)
-                .setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
-                .setShareableActivityToken(new Binder())
+                .setAssistToken(new Binder()).setShareableActivityToken(new Binder())
                 .build();
 
         writeAndPrepareForReading(item);
@@ -359,23 +353,6 @@
         assertTrue(transaction.equals(result));
     }
 
-    @Test
-    public void testFixedRotationAdjustments() {
-        ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(),
-                null /* activityToken */);
-        transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(),
-                new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080,
-                        DisplayCutout.NO_CUTOUT)));
-
-        writeAndPrepareForReading(transaction);
-
-        // Read from parcel and assert
-        ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
-        assertEquals(transaction.hashCode(), result.hashCode());
-        assertTrue(transaction.equals(result));
-    }
-
     /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */
     private void writeAndPrepareForReading(Parcelable parcelable) {
         parcelable.writeToParcel(mParcel, 0 /* flags */);
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 328429c..e690da2 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.ActivityManager;
 import android.app.activity.LocalProvider;
@@ -58,10 +59,12 @@
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
+        final PackageManager pm = mContext.getPackageManager();
+        assumeTrue("device doesn't have the " + PackageManager.FEATURE_MANAGED_USERS + " feature",
+                pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS));
         mUm = UserManager.get(mContext);
         final UserInfo userInfo = createUser();
         mCrossUserId = userInfo.id;
-        final PackageManager pm = mContext.getPackageManager();
         pm.installExistingPackageAsUser(mContext.getPackageName(), mCrossUserId);
         unlockUser();
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 34a8bde..87c167c 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -281,37 +281,6 @@
     }
 
     @SmallTest
-    public void testOverrideDisplayAdjustments() {
-        final int originalOverrideDensity = 200;
-        final int overrideDisplayDensity = 400;
-        final Binder token = new Binder();
-        final Configuration overrideConfig = new Configuration();
-        overrideConfig.densityDpi = originalOverrideDensity;
-        final Resources resources = mResourcesManager.createBaseTokenResources(
-                token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */,
-                null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
-                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */,
-                null /* loaders */);
-
-        // Update the override.
-        boolean handled = mResourcesManager.overrideTokenDisplayAdjustments(token,
-                adjustments -> adjustments.getConfiguration().densityDpi = overrideDisplayDensity);
-
-        assertTrue(handled);
-        assertTrue(resources.hasOverrideDisplayAdjustments());
-        assertEquals(overrideDisplayDensity,
-                resources.getDisplayAdjustments().getConfiguration().densityDpi);
-
-        // Clear the override.
-        handled = mResourcesManager.overrideTokenDisplayAdjustments(token, null /* override */);
-
-        assertTrue(handled);
-        assertFalse(resources.hasOverrideDisplayAdjustments());
-        assertEquals(originalOverrideDensity,
-                resources.getDisplayAdjustments().getConfiguration().densityDpi);
-    }
-
-    @SmallTest
     public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() {
         Binder activity = new Binder();
 
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
index 3cf1722..afbf8db 100644
--- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -19,9 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.res.Configuration;
-import android.graphics.Point;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -70,48 +67,4 @@
 
         assertEquals(configuration, newAdjustments.getConfiguration());
     }
-
-    @Test
-    public void testFixedRotationAdjustments() {
-        final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
-        final int realRotation = Surface.ROTATION_0;
-        final int fixedRotation = Surface.ROTATION_90;
-
-        final int appWidth = 1080;
-        final int appHeight = 1920;
-        mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments(
-                fixedRotation, appWidth, appHeight, null /* cutout */));
-
-        final int w = 1000;
-        final int h = 2000;
-        final Point size = new Point(w, h);
-        mDisplayAdjustments.adjustSize(size, realRotation);
-
-        assertEquals(fixedRotation, mDisplayAdjustments.getRotation(realRotation));
-        assertEquals(new Point(h, w), size);
-
-        final DisplayMetrics metrics = new DisplayMetrics();
-        metrics.xdpi = metrics.noncompatXdpi = w;
-        metrics.widthPixels = metrics.noncompatWidthPixels = w;
-        metrics.ydpi = metrics.noncompatYdpi = h;
-        metrics.heightPixels = metrics.noncompatHeightPixels = h;
-
-        final DisplayMetrics flippedMetrics = new DisplayMetrics();
-        // The physical dpi should not be adjusted.
-        flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w;
-        flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
-        flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h;
-        flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;
-
-        mDisplayAdjustments.adjustMetrics(metrics, realRotation);
-
-        assertEquals(flippedMetrics, metrics);
-
-        mDisplayAdjustments.adjustGlobalAppMetrics(metrics);
-
-        assertEquals(appWidth, metrics.widthPixels);
-        assertEquals(appWidth, metrics.noncompatWidthPixels);
-        assertEquals(appHeight, metrics.heightPixels);
-        assertEquals(appHeight, metrics.noncompatHeightPixels);
-    }
 }
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 74cdd21..10f6f1f 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -16,3 +16,6 @@
 
 # Scroll Capture
 per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+
+# Stylus
+per-file stylus/* = file:/core/java/android/text/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/SurfaceControlViewHostInsetsTest.java b/core/tests/coretests/src/android/view/SurfaceControlViewHostInsetsTest.java
new file mode 100644
index 0000000..4731e81
--- /dev/null
+++ b/core/tests/coretests/src/android/view/SurfaceControlViewHostInsetsTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SurfaceControlViewHostInsetsTest {
+    SurfaceControlViewHost mSurfaceControlViewHost;
+    private boolean mStatusBarIsVisible = false;
+    private Insets mStatusBarInsets;
+    private Instrumentation mInstrumentation;
+
+    private void createViewHierarchy() {
+        Context context = mInstrumentation.getTargetContext();
+
+        View v = new View(context);
+        v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                public WindowInsets onApplyWindowInsets(View v, WindowInsets w) {
+                    mStatusBarIsVisible = w.isVisible(WindowInsets.Type.statusBars());
+                    mStatusBarInsets = w.getInsets(WindowInsets.Type.statusBars());
+                    return w;
+                }
+        });
+        mSurfaceControlViewHost = new SurfaceControlViewHost(context,
+            context.getDisplayNoVerify(), new Binder());
+        mSurfaceControlViewHost.setView(v, 100, 100);
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mInstrumentation.runOnMainSync(() -> { createViewHierarchy(); });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private InsetsState statusBarState(boolean visible) {
+        final InsetsState insetsState = new InsetsState();
+        insetsState.setDisplayFrame(new Rect(0, 0, 1000, 1000));
+        insetsState.getSource(ITYPE_STATUS_BAR).setVisible(visible);
+        insetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
+        return insetsState;
+    }
+
+    private InsetsState statusBarVisibleState() {
+        return statusBarState(true);
+    }
+
+    private void sendInsetsSync(InsetsState s, Rect f) {
+        try  {
+            mSurfaceControlViewHost.getSurfacePackage().getRemoteInterface()
+                .onInsetsChanged(s, f);
+        } catch (Exception e) {
+        }
+        mInstrumentation.waitForIdleSync();
+    }
+
+    @Test
+    public void sendInsetsToSurfaceControlViewHost() {
+        final InsetsState insetsState = statusBarVisibleState();
+        sendInsetsSync(insetsState, new Rect(0, 0, 100, 100));
+        assertTrue(mStatusBarIsVisible);
+
+        final InsetsState insetsState2 = statusBarState(false);
+        sendInsetsSync(insetsState2, new Rect(0, 0, 100, 100));
+        assertFalse(mStatusBarIsVisible);
+   }
+
+    @Test
+    public void insetsAreRelativeToFrame() {
+        final InsetsState insetsState = statusBarVisibleState();
+        sendInsetsSync(insetsState, new Rect(0, 0, 100, 100));
+
+        assertTrue(mStatusBarIsVisible);
+        assertEquals(10, mStatusBarInsets.top);
+
+        sendInsetsSync(insetsState, new Rect(0, 5, 100, 100));
+        assertEquals(5, mStatusBarInsets.top);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
similarity index 96%
rename from core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
rename to core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 632a1a9..e11fe17 100644
--- a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.view;
+package android.view.stylus;
 
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
@@ -33,6 +33,12 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.view.HandwritingInitiator;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -47,7 +53,7 @@
  * Tests for {@link HandwritingInitiator}
  *
  * Build/Install/Run:
- *  atest FrameworksCoreTests:HandwritingInitiatorTest
+ *  atest FrameworksCoreTests:android.view.stylus.HandwritingInitiatorTest
  */
 @Presubmit
 @SmallTest
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 269d842..f8db069 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -297,8 +297,7 @@
                     null /* voiceInteractor */, null /* state */, null /* persistentState */,
                     null /* pendingResults */, null /* pendingNewIntents */,
                     null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
-                    mThread /* client */, null /* asssitToken */,
-                    null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
+                    mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */,
                     false /* launchedFromBubble */);
         }
 
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
index f8dd153..0c939ec 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTest.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -27,6 +27,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -34,7 +35,6 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,8 +49,6 @@
 import org.mockito.Mockito;
 import org.mockito.quality.Strictness;
 
-import java.util.function.Consumer;
-
 /**
  * Tests for {@link Display}.
  *
@@ -96,14 +94,12 @@
 
         // Ensure no adjustments are set before each test.
         mApplicationContext = ApplicationProvider.getApplicationContext();
-        DisplayAdjustments displayAdjustments =
-                mApplicationContext.getResources().getDisplayAdjustments();
-        displayAdjustments.setFixedRotationAdjustments(null);
-        mApplicationContext.getResources().overrideDisplayAdjustments(null);
         mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
                 null);
         mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
                 null);
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setDisplayRotation(WindowConfiguration.ROTATION_UNDEFINED);
         mDisplayInfo.rotation = ROTATION_0;
 
         mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
@@ -151,41 +147,11 @@
     }
 
     @Test
-    public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-        // GIVEN display is constructed with display adjustments.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                displayAdjustments);
-        // THEN rotation is not adjusted since no override was set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is not adjusted since no override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
     public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+        setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
         // GIVEN display is constructed with default resources.
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
@@ -234,37 +200,11 @@
     }
 
     @Test
-    public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
     public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+        setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_0);
         // GIVEN display is constructed with default resources.
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
@@ -277,7 +217,7 @@
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+        setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
         // GIVEN display is constructed with default resources.
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
@@ -380,37 +320,11 @@
     }
 
     @Test
-    public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
     public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN fixed rotation adjustments are rotated with an override.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+        setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_0);
         // GIVEN display is constructed with default resources.
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
@@ -423,7 +337,7 @@
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN fixed rotation adjustments are rotated.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+        setLocalDisplayInConfig(mApplicationContext.getResources(), ROTATION_90);
         // GIVEN display is constructed with default resources.
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
@@ -569,27 +483,8 @@
         assertThat(metrics.heightPixels).isEqualTo(bounds.height());
     }
 
-    private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
-            Resources resources, @Surface.Rotation int rotation) {
-        FixedRotationAdjustments fixedRotationAdjustments =
-                setFixedRotationAdjustments(resources, rotation);
-        resources.overrideDisplayAdjustments(
-                buildOverrideRotationAdjustments(fixedRotationAdjustments));
-        return fixedRotationAdjustments;
-    }
-
-    private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+    private static void setLocalDisplayInConfig(Resources resources,
             @Surface.Rotation int rotation) {
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
-        return fixedRotationAdjustments;
-    }
-
-    private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
-            FixedRotationAdjustments fixedRotationAdjustments) {
-        return consumedDisplayAdjustments
-                -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+        resources.getConfiguration().windowConfiguration.setDisplayRotation(rotation);
     }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e68b1ac..b3dcc34 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -334,6 +334,7 @@
         <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_GAME_MODE"/>
+        <permission name="android.permission.MANAGE_LOW_POWER_STANDBY" />
         <permission name="android.permission.MANAGE_ROLLBACKS"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 84cf285a..76b4036 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -36,16 +36,16 @@
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
     <string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
-    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
+    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు ఫుల్-స్క్రీన్‌"</string>
     <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ఎడమవైపు 70%"</string>
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ఎడమవైపు 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ఎడమవైపు 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు పూర్తి స్క్రీన్"</string>
-    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ పూర్తి స్క్రీన్"</string>
+    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు ఫుల్-స్క్రీన్‌"</string>
+    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ ఫుల్-స్క్రీన్‌"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ఎగువ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగువ 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ఎగువ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ పూర్తి స్క్రీన్"</string>
+    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ ఫుల్-స్క్రీన్‌"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"వన్-హ్యాండెడ్ మోడ్‌ను ఉపయోగించడం"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"నిష్క్రమించడానికి, స్క్రీన్ కింది భాగం నుండి పైకి స్వైప్ చేయండి లేదా యాప్ పైన ఎక్కడైనా ట్యాప్ చేయండి"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"వన్-హ్యాండెడ్ మోడ్‌ను ప్రారంభిస్తుంది"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 13e81bd..79c8a87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -371,10 +371,14 @@
                 }
 
                 if (a.getShowBackground()) {
-                    // use the window's background color if provided as the background color for the
-                    // animation - the top most window with a valid background color and
-                    // showBackground set takes precedence.
-                    if (change.getBackgroundColor() != 0) {
+                    if (info.getAnimationOptions().getBackgroundColor() != 0) {
+                        // If available use the background color provided through AnimationOptions
+                        backgroundColorForTransition =
+                                info.getAnimationOptions().getBackgroundColor();
+                    } else if (change.getBackgroundColor() != 0) {
+                        // Otherwise default to the window's background color if provided through
+                        // the theme as the background color for the animation - the top most window
+                        // with a valid background color and showBackground set takes precedence.
                         backgroundColorForTransition = change.getBackgroundColor();
                     }
                 }
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
index 7126bfa..23d107b 100644
--- a/libs/tracingproxy/Android.bp
+++ b/libs/tracingproxy/Android.bp
@@ -37,6 +37,7 @@
 
     srcs: [
         ":ITracingServiceProxy.aidl",
+        ":TraceReportParams.aidl",
     ],
 
     shared_libs: [
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 8054fd4..f4e965f 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -125,8 +125,8 @@
     boolean isAdasGnssLocationEnabledForUser(int userId);
     void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
 
-    boolean isAutoGnssSuspended();
-    void setAutoGnssSuspended(boolean suspended);
+    boolean isAutomotiveGnssSuspended();
+    void setAutomotiveGnssSuspended(boolean suspended);
 
     void addTestProvider(String name, in ProviderProperties properties,
         in List<String> locationTags, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 9109a18..d275628 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -758,45 +758,41 @@
     }
 
     /**
-     * Set whether GNSS requests are suspended on the device.
+     * Set whether GNSS requests are suspended on the automotive device.
      *
-     * This method was added to help support power management use cases on automotive devices. More
-     * specifically, it is being added to fix a suspend to RAM issue where the SoC can't go into
-     * a lower power state when applications are actively requesting GNSS updates.
+     * For devices where GNSS prevents the system from going into a low power state, GNSS should
+     * be suspended right before going into the lower power state and resumed right after the device
+     * wakes up.
      *
-     * Ideally, the issue should be fixed at a lower layer in the stack, but this API introduces a
-     * workaround in the platform layer. This API allows car specific services to halt GNSS requests
-     * based on changes to the car power policy, which will in turn enable the device to go into
-     * suspend.
+     * This method disables GNSS and should only be used for power management use cases such as
+     * suspend-to-RAM or suspend-to-disk.
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
-    public void setAutoGnssSuspended(boolean suspended) {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+    public void setAutomotiveGnssSuspended(boolean suspended) {
         try {
-            mService.setAutoGnssSuspended(suspended);
+            mService.setAutomotiveGnssSuspended(suspended);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Return whether GNSS requests are suspended or not.
-     *
-     * This method was added to help support power management use cases on automotive devices. More
-     * specifically, it is being added as part of the fix for a suspend to RAM issue where the SoC
-     * can't go into a lower power state when applications are actively requesting GNSS updates.
+     * Return whether GNSS requests are suspended on the automotive device.
      *
      * @return true if GNSS requests are suspended and false if they aren't.
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
-    public boolean isAutoGnssSuspended() {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+    public boolean isAutomotiveGnssSuspended() {
         try {
-            return mService.isAutoGnssSuspended();
+            return mService.isAutomotiveGnssSuspended();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index b4fdcb9..3dbb8e09 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -19,6 +19,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
@@ -526,9 +529,17 @@
     }
 
     /**
-     * Returns all encoder profiles of a camcorder profile for the given camera at
-     * the given quality level.
-     *
+     * This change id controls the kind of video profiles returned by {@link #getAll}.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long RETURN_ADVANCED_VIDEO_PROFILES = 206033068L; // buganizer id
+
+    /**
+     * Returns all basic encoder profiles of a camcorder profile for
+     * the given camera at the given quality level.
+     * <p>
      * Quality levels QUALITY_LOW, QUALITY_HIGH are guaranteed to be supported, while
      * other levels may or may not be supported. The supported levels can be checked using
      * {@link #hasProfile(int, int)}.
@@ -537,19 +548,26 @@
      * QUALITY_LOW/QUALITY_HIGH have to match one of qcif, cif, 480p, 720p, 1080p or 2160p.
      * E.g. if the device supports 480p, 720p, 1080p and 2160p, then low is 480p and high is
      * 2160p.
-     *
+     * <p>
      * The same is true for time lapse quality levels, i.e. QUALITY_TIME_LAPSE_LOW,
      * QUALITY_TIME_LAPSE_HIGH are guaranteed to be supported and have to match one of
      * qcif, cif, 480p, 720p, 1080p, or 2160p.
-     *
+     * <p>
      * For high speed quality levels, they may or may not be supported. If a subset of the levels
      * are supported, QUALITY_HIGH_SPEED_LOW and QUALITY_HIGH_SPEED_HIGH are guaranteed to be
      * supported and have to match one of 480p, 720p, or 1080p.
-     *
+     * <p>
      * A camcorder recording session with higher quality level usually has higher output
      * bit rate, better video and/or audio recording quality, larger video frame
      * resolution and higher audio sampling rate, etc, than those with lower quality
      * level.
+     * <p>
+     * <b>Note:</b> as of {@link android.os.Build.VERSION_CODES#TIRAMISU Android TIRAMISU},
+     * this method can return advanced encoder profiles.
+     * <p>Apps targeting {@link Build.VERSION_CODES#S_V2} or before will only receive basic
+     * video encoder profiles that use output YUV 4:2:0 8-bit content.
+     * Apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above will also receive advanced
+     * video encoder profiles that may output 10-bit, YUV 4:2:2/4:4:4 or HDR content.
      *
      * @param cameraId the id for the camera. Numeric camera ids from the list received by invoking
      *                 {@link CameraManager#getCameraIdList} can be used as long as they are
@@ -602,7 +620,9 @@
         } catch (NumberFormatException e) {
             return null;
         }
-        return native_get_camcorder_profiles(id, quality);
+        return native_get_camcorder_profiles(
+                id, quality,
+                CompatChanges.isChangeEnabled(RETURN_ADVANCED_VIDEO_PROFILES));
     }
 
     /**
@@ -712,7 +732,7 @@
     private static native final CamcorderProfile native_get_camcorder_profile(
             int cameraId, int quality);
     private static native final EncoderProfiles native_get_camcorder_profiles(
-            int cameraId, int quality);
+            int cameraId, int quality, boolean advanced);
     private static native final boolean native_has_camcorder_profile(
             int cameraId, int quality);
 }
diff --git a/media/java/android/media/EncoderProfiles.java b/media/java/android/media/EncoderProfiles.java
index ac8c65e..3e26fc6f 100644
--- a/media/java/android/media/EncoderProfiles.java
+++ b/media/java/android/media/EncoderProfiles.java
@@ -16,8 +16,11 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -97,6 +100,12 @@
                 return MediaFormat.MIMETYPE_VIDEO_VP8;
             } else if (codec == MediaRecorder.VideoEncoder.HEVC) {
                 return MediaFormat.MIMETYPE_VIDEO_HEVC;
+            } else if (codec == MediaRecorder.VideoEncoder.VP9) {
+                return MediaFormat.MIMETYPE_VIDEO_VP9;
+            } else if (codec == MediaRecorder.VideoEncoder.DOLBY_VISION) {
+                return MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION;
+            } else if (codec == MediaRecorder.VideoEncoder.AV1) {
+                return MediaFormat.MIMETYPE_VIDEO_AV1;
             }
             // we should never be here
             throw new RuntimeException("Unknown codec");
@@ -190,19 +199,73 @@
             return profile;
         }
 
+        /**
+         * The bit depth of the encoded video.
+         * <p>
+         * This value is effectively 8 or 10, but some devices may
+         * support additional values.
+         */
+        public int getBitDepth() {
+            return bitDepth;
+        }
+
+        /**
+         * The chroma subsampling of the encoded video.
+         * <p>
+         * For most devices this is always YUV_420 but some devices may
+         * support additional values.
+         *
+         * @see #YUV_420
+         * @see #YUV_422
+         * @see #YUV_444
+         */
+        public @ChromaSubsampling int getChromaSubsampling() {
+            return chromaSubsampling;
+        }
+
+        /**
+         * The HDR format of the encoded video.
+         * <p>
+         * This is one of the HDR_ values.
+         * @see #HDR_NONE
+         * @see #HDR_HLG
+         * @see #HDR_HDR10
+         * @see #HDR_HDR10PLUS
+         * @see #HDR_DOLBY_VISION
+         */
+        public @HdrFormat int getHdrFormat() {
+            return hdrFormat;
+        }
+
         // Constructor called by JNI and CamcorderProfile
         /* package private */ VideoProfile(int codec,
                              int width,
                              int height,
                              int frameRate,
                              int bitrate,
-                             int profile) {
+                             int profile,
+                             int chromaSubsampling,
+                             int bitDepth,
+                             int hdrFormat) {
             this.codec = codec;
             this.width = width;
             this.height = height;
             this.frameRate = frameRate;
             this.bitrate = bitrate;
             this.profile = profile;
+            this.chromaSubsampling = chromaSubsampling;
+            this.bitDepth = bitDepth;
+            this.hdrFormat = hdrFormat;
+        }
+
+        /* package private */ VideoProfile(int codec,
+                             int width,
+                             int height,
+                             int frameRate,
+                             int bitrate,
+                             int profile) {
+            this(codec, width, height, frameRate, bitrate, profile,
+                 YUV_420, 8 /* bitDepth */, HDR_NONE);
         }
 
         private int codec;
@@ -211,6 +274,81 @@
         private int frameRate;
         private int bitrate;
         private int profile;
+        private int chromaSubsampling;
+        private int bitDepth;
+        private int hdrFormat;
+
+        /** @hide */
+        @IntDef({
+            HDR_NONE,
+            HDR_HLG,
+            HDR_HDR10,
+            HDR_HDR10PLUS,
+            HDR_DOLBY_VISION,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface HdrFormat {}
+
+        /** Not HDR (SDR).
+         *  <p>
+         *  An HDR format specifying SDR (Standard Dynamic
+         *  Range) recording. */
+        public static final int HDR_NONE = 0;
+
+        /** HLG (Hybrid-Log Gamma).
+         *  <p>
+         *  An HDR format specifying HLG. */
+        public static final int HDR_HLG = 1;
+
+        /** HDR10.
+         *  <p>
+         *  An HDR format specifying HDR10. */
+        public static final int HDR_HDR10 = 2;
+
+        /** HDR10+.
+         *  <p>
+         *  An HDR format specifying HDR10+. */
+        public static final int HDR_HDR10PLUS = 3;
+
+        /**
+         *  Dolby Vision
+         *  <p>
+         *  An HDR format specifying Dolby Vision. For this format
+         *  the codec is always a Dolby Vision encoder. The encoder
+         *  profile specifies which Dolby Vision version is being
+         *  used.
+         *
+         *  @see #getProfile
+         */
+        public static final int HDR_DOLBY_VISION = 4;
+
+        /** @hide */
+        @IntDef({
+            YUV_420,
+            YUV_422,
+            YUV_444,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ChromaSubsampling {}
+
+
+        /** YUV 4:2:0.
+         *  <p>
+         *  A chroma subsampling where the U and V planes are subsampled
+         *  by 2 both horizontally and vertically. */
+        public static final int YUV_420 = 0;
+
+        /** YUV 4:2:2.
+         *  <p>
+         *  A chroma subsampling where the U and V planes are subsampled
+         *  by 2 horizontally alone. */
+        public static final int YUV_422 = 1;
+
+        /** YUV 4:4:4.
+         *  <p>
+         *  A chroma subsampling where the U and V planes are not
+         *  subsampled. */
+        public static final int YUV_444 = 2;
     }
 
     /**
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 5891a18..e18642c 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2526,7 +2526,7 @@
             }
             if (datetime == null) return -1;
             return datetime.getTime();
-        } catch (IllegalArgumentException e) {
+        } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
             return -1;
         }
     }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 5dee945..5e300c8 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -4044,6 +4044,12 @@
         public static final int DolbyVisionLevelUhd30   = 0x40;
         public static final int DolbyVisionLevelUhd48   = 0x80;
         public static final int DolbyVisionLevelUhd60   = 0x100;
+        @SuppressLint("AllUpper")
+        public static final int DolbyVisionLevelUhd120  = 0x200;
+        @SuppressLint("AllUpper")
+        public static final int DolbyVisionLevel8k30    = 0x400;
+        @SuppressLint("AllUpper")
+        public static final int DolbyVisionLevel8k60    = 0x800;
 
         // Profiles and levels for AV1 Codec, corresponding to the definitions in
         // "AV1 Bitstream & Decoding Process Specification", Annex A
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index d7857a0..f1e6038 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -705,6 +705,9 @@
         public static final int MPEG_4_SP = 3;
         public static final int VP8 = 4;
         public static final int HEVC = 5;
+        public static final int VP9 = 6;
+        public static final int DOLBY_VISION = 7;
+        public static final int AV1 = 8;
     }
 
     /**
@@ -717,6 +720,9 @@
         VideoEncoder.MPEG_4_SP,
         VideoEncoder.VP8,
         VideoEncoder.HEVC,
+        VideoEncoder.VP9,
+        VideoEncoder.DOLBY_VISION,
+        VideoEncoder.AV1,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface VideoEncoderValues {}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 41666c7..75236f4 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1851,6 +1851,7 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
     public int getClientPid(@NonNull String sessionId) {
         return getClientPidInternal(sessionId);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ffc4433..1e32cad 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -68,6 +68,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -1602,25 +1603,27 @@
      *
      * @param statusTypes an array of status types.
      *
-     * @return an array of current readiness states. {@code null} if the operation failed or
-     *         unsupported versions.
+     * @return a list of current readiness states. It is empty if the operation fails or unsupported
+     *         versions.
      * @throws IllegalStateException if there is no active frontend currently.
      */
-    @Nullable
-    @SuppressLint("ArrayReturn")
-    @SuppressWarnings("NullableCollection")
-    public FrontendStatusReadiness[] getFrontendStatusReadiness(
+    @NonNull
+    public List<FrontendStatusReadiness> getFrontendStatusReadiness(
             @NonNull @FrontendStatusType int[] statusTypes) {
         mFrontendLock.lock();
         try {
             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
-                        TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
-                return null;
+                        TunerVersionChecker.TUNER_VERSION_2_0, "Get fronted status readiness")) {
+                return Collections.EMPTY_LIST;
             }
             if (mFrontend == null) {
                 throw new IllegalStateException("frontend is not initialized");
             }
-            return nativeGetFrontendStatusReadiness(statusTypes);
+            FrontendStatusReadiness[] readiness = nativeGetFrontendStatusReadiness(statusTypes);
+            if (readiness == null) {
+                return Collections.EMPTY_LIST;
+            }
+            return Arrays.asList(readiness);
         } finally {
             mFrontendLock.unlock();
         }
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
index 52527b3..c30753c 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatusReadiness.java
@@ -30,7 +30,7 @@
  * @hide
  */
 @SystemApi
-public class FrontendStatusReadiness {
+public final class FrontendStatusReadiness {
     /** @hide */
     @IntDef({FRONTEND_STATUS_READINESS_UNDEFINED, FRONTEND_STATUS_READINESS_UNAVAILABLE,
             FRONTEND_STATUS_READINESS_UNSTABLE, FRONTEND_STATUS_READINESS_STABLE,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index feae606..18b779f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -87,6 +87,7 @@
         "android.hardware.drm@1.4",
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
+        "android.hardware.drm-V1-ndk",
     ],
 
     header_libs: [
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index 90325e7..f2c48a0 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -225,7 +225,7 @@
 
 static jobject
 android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject /* thiz */, jint id,
-                                                          jint quality)
+                                                          jint quality, jboolean advanced)
 {
     ALOGV("native_get_camcorder_profiles: %d %d", id, quality);
     if (!isCamcorderQualityKnown(quality)) {
@@ -251,7 +251,7 @@
 
     jclass videoProfileClazz = env->FindClass("android/media/EncoderProfiles$VideoProfile");
     jmethodID videoProfileConstructorMethodID =
-        env->GetMethodID(videoProfileClazz, "<init>", "(IIIIII)V");
+        env->GetMethodID(videoProfileClazz, "<init>", "(IIIIIIIII)V");
 
     jclass audioProfileClazz = env->FindClass("android/media/EncoderProfiles$AudioProfile");
     jmethodID audioProfileConstructorMethodID =
@@ -262,6 +262,16 @@
     {
         int i = 0;
         for (const MediaProfiles::VideoCodec *vc : cp->getVideoCodecs()) {
+            chroma_subsampling cs = vc->getChromaSubsampling();
+            int bitDepth = vc->getBitDepth();
+            hdr_format hdr = vc->getHdrFormat();
+
+            bool isAdvanced =
+                (bitDepth != 8 || cs != CHROMA_SUBSAMPLING_YUV_420 || hdr != HDR_FORMAT_NONE);
+            if (static_cast<bool>(advanced) && !isAdvanced) {
+                continue;
+            }
+
             jobject videoCodec = env->NewObject(videoProfileClazz,
                                                 videoProfileConstructorMethodID,
                                                 vc->getCodec(),
@@ -269,7 +279,10 @@
                                                 vc->getFrameHeight(),
                                                 vc->getFrameRate(),
                                                 vc->getBitrate(),
-                                                vc->getProfile());
+                                                vc->getProfile(),
+                                                static_cast<int>(cs),
+                                                bitDepth,
+                                                static_cast<int>(hdr));
             env->SetObjectArrayElement(videoCodecs, i++, videoCodec);
         }
     }
@@ -400,7 +413,7 @@
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_camcorder_profile",           "(II)Landroid/media/CamcorderProfile;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profile},
-    {"native_get_camcorder_profiles",          "(II)Landroid/media/EncoderProfiles;",
+    {"native_get_camcorder_profiles",          "(IIZ)Landroid/media/EncoderProfiles;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profiles},
     {"native_has_camcorder_profile",           "(II)Z",
                                                                          (void *)android_media_MediaProfiles_native_has_camcorder_profile},
diff --git a/omapi/OWNERS b/omapi/OWNERS
new file mode 100644
index 0000000..5682fd3
--- /dev/null
+++ b/omapi/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 456592
+
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 73b9c72..56faa52 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.service.NetworkIdentityProto;
-import android.telephony.Annotation;
 import android.telephony.TelephonyManager;
 import android.util.proto.ProtoOutputStream;
 
@@ -275,8 +274,7 @@
     @Deprecated
     @NonNull
     public static NetworkIdentity buildNetworkIdentity(Context context,
-            @NonNull NetworkStateSnapshot snapshot,
-            boolean defaultNetwork, @Annotation.NetworkType int ratType) {
+            @NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) {
         final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
                 .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
         if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
@@ -433,7 +431,7 @@
          * @return this builder.
          */
         @NonNull
-        public Builder setRatType(@Annotation.NetworkType int ratType) {
+        public Builder setRatType(int ratType) {
             if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
                     && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN
                     && ratType != NetworkStatsManager.NETWORK_TYPE_5G_NSA) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 27e717f..9b58b01 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
 import android.text.TextUtils;
 import android.util.ArraySet;
 
@@ -203,7 +202,7 @@
      * @hide
      */
     public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
-            @NetworkType int ratType, int metered) {
+            int ratType, int metered) {
         if (TextUtils.isEmpty(subscriberId)) {
             return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
                     null /* matchSubscriberIds */,
@@ -1039,7 +1038,7 @@
          * @return this builder.
          */
         @NonNull
-        public Builder setRatType(@NetworkType int ratType) {
+        public Builder setRatType(int ratType) {
             // Input will be validated with the match rule when building the template.
             mRatType = ratType;
             return this;
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
index 1eb52fb..8bb12a6d 100644
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -111,21 +111,6 @@
         public int statsUid = -1;
     }
 
-    public static void setKernelCounterSet(int uid, int counterSet) {
-        final int errno = native_setCounterSet(counterSet, uid);
-        if (errno < 0) {
-            Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
-                    + errno);
-        }
-    }
-
-    public static void resetKernelUidStats(int uid) {
-        int errno = native_deleteTagData(0, uid);
-        if (errno < 0) {
-            Log.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
-        }
-    }
-
     /**
      * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
      * format like {@code 0x7fffffff00000000}.
@@ -141,6 +126,4 @@
 
     private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
     private static native int native_untagSocketFd(FileDescriptor fd);
-    private static native int native_setCounterSet(int uid, int counterSetNum);
-    private static native int native_deleteTagData(int tag, int uid);
 }
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 5c069e9..748b0ae 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -59,8 +59,6 @@
 
 import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
 import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
-import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
-import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -121,6 +119,8 @@
 import android.provider.Settings.Global;
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
 import android.text.TextUtils;
@@ -139,10 +139,14 @@
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.BestClock;
 import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.Struct.U32;
+import com.android.net.module.util.Struct.U8;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -207,6 +211,10 @@
     private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
             "netstats_combine_subtype_enabled";
 
+    // This is current path but may be changed soon.
+    private static final String UID_COUNTERSET_MAP_PATH =
+            "/sys/fs/bpf/map_netd_uid_counterset_map";
+
     private final Context mContext;
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
@@ -325,8 +333,14 @@
     @GuardedBy("mStatsLock")
     private NetworkStatsCollection mXtStatsCached;
 
-    /** Current counter sets for each UID. */
+    /**
+     * Current counter sets for each UID.
+     * TODO: maybe remove mActiveUidCounterSet and read UidCouneterSet value from mUidCounterSetMap
+     * directly ? But if mActiveUidCounterSet would be accessed very frequently, maybe keep
+     * mActiveUidCounterSet to avoid accessing kernel too frequently.
+     */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
+    private final IBpfMap<U32, U8> mUidCounterSetMap;
 
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mUidOperations = new NetworkStats(0L, 10);
@@ -459,6 +473,7 @@
         mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
         mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
         mInterfaceMapUpdater.start();
+        mUidCounterSetMap = mDeps.getUidCounterSetMap();
     }
 
     /**
@@ -520,6 +535,21 @@
                 @NonNull Context ctx, @NonNull Handler handler) {
             return new BpfInterfaceMapUpdater(ctx, handler);
         }
+
+        /** Get counter sets map for each UID. */
+        public IBpfMap<U32, U8> getUidCounterSetMap() {
+            try {
+                return new BpfMap<U32, U8>(UID_COUNTERSET_MAP_PATH, BpfMap.BPF_F_RDWR,
+                        U32.class, U8.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create uid counter set map: " + e);
+                return null;
+            }
+        }
+
+        public TagStatsDeleter getTagStatsDeleter() {
+            return NetworkStatsService::nativeDeleteTagData;
+        }
     }
 
     /**
@@ -1077,6 +1107,29 @@
         }
     }
 
+    private void setKernelCounterSet(int uid, int set) {
+        if (mUidCounterSetMap == null) {
+            Log.wtf(TAG, "Fail to set UidCounterSet: Null bpf map");
+            return;
+        }
+
+        if (set == SET_DEFAULT) {
+            try {
+                mUidCounterSetMap.deleteEntry(new U32(uid));
+            } catch (ErrnoException e) {
+                Log.w(TAG, "UidCounterSetMap.deleteEntry(" + uid + ") failed with errno: " + e);
+            }
+            return;
+        }
+
+        try {
+            mUidCounterSetMap.updateEntry(new U32(uid), new U8((short) set));
+        } catch (ErrnoException e) {
+            Log.w(TAG, "UidCounterSetMap.updateEntry(" + uid + ", " + set
+                    + ") failed with errno: " + e);
+        }
+    }
+
     @VisibleForTesting
     public void setUidForeground(int uid, boolean uidForeground) {
         PermissionUtils.enforceNetworkStackPermission(mContext);
@@ -1752,7 +1805,10 @@
 
         // Clear kernel stats associated with UID
         for (int uid : uids) {
-            resetKernelUidStats(uid);
+            final int ret = mDeps.getTagStatsDeleter().deleteTagData(uid);
+            if (ret < 0) {
+                Log.w(TAG, "problem clearing counters for uid " + uid + ": " + Os.strerror(-ret));
+            }
         }
     }
 
@@ -2331,4 +2387,12 @@
     private static native long nativeGetTotalStat(int type);
     private static native long nativeGetIfaceStat(String iface, int type);
     private static native long nativeGetUidStat(int uid, int type);
+
+    // TODO: use BpfNetMaps to delete tag data and remove this.
+    @VisibleForTesting
+    interface TagStatsDeleter {
+        int deleteTagData(int uid);
+    }
+
+    private static native int nativeDeleteTagData(int uid);
 }
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 3e35e603..5bba0b1 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -24,7 +24,6 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.telephony.Annotation;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
@@ -59,8 +58,7 @@
          * @param collapsedRatType collapsed RAT type.
          *                     @see android.app.usage.NetworkStatsManager#getCollapsedRatType(int).
          */
-        void onCollapsedRatTypeChanged(@NonNull String subscriberId,
-                @Annotation.NetworkType int collapsedRatType);
+        void onCollapsedRatTypeChanged(@NonNull String subscriberId, int collapsedRatType);
     }
     private final Delegate mDelegate;
 
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index e51bb45..3eb6ea9 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -42,6 +42,7 @@
     @VisibleForTesting
     View.OnClickListener mLearnMoreListener;
     private CharSequence mContentDescription;
+    private CharSequence mLearnMoreText;
     private CharSequence mLearnMoreContentDescription;
     private FooterLearnMoreSpan mLearnMoreSpan;
 
@@ -69,7 +70,12 @@
         TextView learnMore = holder.itemView.findViewById(R.id.settingslib_learn_more);
         if (learnMore != null && mLearnMoreListener != null) {
             learnMore.setVisibility(View.VISIBLE);
-            SpannableString learnMoreText = new SpannableString(learnMore.getText());
+            if (TextUtils.isEmpty(mLearnMoreText)) {
+                mLearnMoreText = learnMore.getText();
+            } else {
+                learnMore.setText(mLearnMoreText);
+            }
+            SpannableString learnMoreText = new SpannableString(mLearnMoreText);
             if (mLearnMoreSpan != null) {
                 learnMoreText.removeSpan(mLearnMoreSpan);
             }
@@ -123,6 +129,18 @@
     }
 
     /**
+     * Sets the learn more text.
+     *
+     * @param learnMoreText The string of the learn more text.
+     */
+    public void setLearnMoreText(CharSequence learnMoreText) {
+        if (!TextUtils.equals(mLearnMoreText, learnMoreText)) {
+            mLearnMoreText = learnMoreText;
+            notifyChanged();
+        }
+    }
+
+    /**
      * To set content description of the learn more text. This can use for talkback
      * environment if developer wants to have a customization content.
      *
diff --git a/packages/SettingsLib/res/drawable/add_a_photo_circled.xml b/packages/SettingsLib/res/drawable/add_a_photo_circled.xml
new file mode 100644
index 0000000..bcfd221
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/add_a_photo_circled.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="oval">
+            <solid android:color="?android:attr/colorAccent"/>
+        </shape>
+    </item>
+    <item
+        android:left="@dimen/add_a_photo_circled_padding"
+        android:right="@dimen/add_a_photo_circled_padding"
+        android:top="@dimen/add_a_photo_circled_padding"
+        android:bottom="@dimen/add_a_photo_circled_padding"
+        android:drawable="@drawable/ic_add_a_photo"/>
+</layer-list>
diff --git a/packages/SettingsLib/res/drawable/ic_add_a_photo.xml b/packages/SettingsLib/res/drawable/ic_add_a_photo.xml
new file mode 100644
index 0000000..4e35503
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_add_a_photo.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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:attr/colorPrimary"
+        android:pathData="M11.5,17.5q1.875,0 3.188,-1.313Q16,14.876 16,13q0,-1.875 -1.313,-3.188Q13.376,8.5 11.5,8.5q-1.875,0 -3.188,1.313Q7,11.124 7,13q0,1.875 1.313,3.188Q9.624,17.5 11.5,17.5zM3.5,21q-0.825,0 -1.413,-0.587Q1.5,19.825 1.5,19L1.5,7q0,-0.825 0.587,-1.412Q2.675,5 3.5,5h3.15L8.5,3h6v4h-11v12h16v-9h2v9q0,0.825 -0.587,1.413Q20.325,21 19.5,21zM18.5,8L18.5,6h-2L16.5,4h2L18.5,2h2v2h2v2h-2v2zM11.5,13z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index f66ff00..c8ddcc8 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -18,24 +18,32 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:baselineAligned="false"
-    android:orientation="horizontal"
+    android:orientation="vertical"
     android:padding="16dp">
 
-    <ImageView
-        android:id="@+id/user_photo"
-        android:layout_width="56dp"
-        android:layout_height="56dp"
-        android:layout_gravity="bottom"
-        android:contentDescription="@string/user_image_photo_selector"
-        android:background="@*android:drawable/spinner_background_holo_dark"
-        android:scaleType="fitCenter"/>
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center">
+        <ImageView
+            android:id="@+id/user_photo"
+            android:layout_width="@dimen/user_photo_size_in_profile_info_dialog"
+            android:layout_height="@dimen/user_photo_size_in_profile_info_dialog"
+            android:contentDescription="@string/user_image_photo_selector"
+            android:scaleType="fitCenter"/>
+        <ImageView
+            android:id="@+id/add_a_photo_icon"
+            android:layout_width="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
+            android:layout_height="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
+            android:src="@drawable/add_a_photo_circled"
+            android:layout_gravity="bottom|right" />
+    </FrameLayout>
 
     <EditText
         android:id="@+id/user_name"
-        android:layout_width="0dp"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_weight="1"
+        android:layout_gravity="center"
         android:minWidth="200dp"
         android:layout_marginStart="6dp"
         android:minHeight="@dimen/min_tap_target_size"
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9bccc3f..9ee42b6 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -98,4 +98,8 @@
     <!-- Minimum width for the popup for updating a user's photo. -->
     <dimen name="update_user_photo_popup_min_width">300dp</dimen>
 
+    <dimen name="add_a_photo_circled_padding">6dp</dimen>
+    <dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen>
+    <dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e4eab4b..5ada028 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1164,6 +1164,9 @@
     <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
     <string name="disabled_by_admin_summary_text">Controlled by admin</string>
 
+    <!-- Summary for settings preference disabled by app ops [CHAR LIMIT=50] -->
+    <string name="disabled_by_app_ops_text">Controlled by Restricted Setting</string>
+
     <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
     <string name="disabled">Disabled</string>
     <!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1e8cb9f..1573edb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -26,14 +26,17 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
@@ -42,6 +45,7 @@
 import android.view.MenuItem;
 import android.widget.TextView;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -729,6 +733,26 @@
     }
 
     /**
+     * Show restricted setting dialog.
+     */
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    public static void sendShowRestrictedSettingDialogIntent(Context context,
+            String packageName, int uid) {
+        final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
+        context.startActivity(intent);
+    }
+
+    /**
+     * Get restricted settings dialog intent.
+     */
+    private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
+        final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        return intent;
+    }
+
+    /**
      * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes.
      * {@link LockPatternUtils} is an internal API not supported by robolectric.
      * {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric.
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index fc8b587..81146fa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -19,6 +19,7 @@
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import android.content.Context;
+import android.os.Process;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.view.View;
@@ -37,9 +38,14 @@
     RestrictedPreferenceHelper mHelper;
 
     public RestrictedPreference(Context context, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
+            int defStyleAttr, int defStyleRes, String packageName, int uid) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+        mHelper = new RestrictedPreferenceHelper(context, this, attrs, packageName, uid);
+    }
+
+    public RestrictedPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        this(context, attrs, defStyleAttr, defStyleRes, null, Process.INVALID_UID);
     }
 
     public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -55,6 +61,11 @@
         this(context, null);
     }
 
+    public RestrictedPreference(Context context, String packageName, int uid) {
+        this(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+                android.R.attr.preferenceStyle), 0, packageName, uid);
+    }
+
     @Override
     protected int getSecondTargetResId() {
         return R.layout.restricted_icon;
@@ -115,7 +126,21 @@
         }
     }
 
+    public void setDisabledByAppOps(boolean disabled) {
+        if (mHelper.setDisabledByAppOps(disabled)) {
+            notifyChanged();
+        }
+    }
+
     public boolean isDisabledByAdmin() {
         return mHelper.isDisabledByAdmin();
     }
+
+    public int getUid() {
+        return mHelper != null ? mHelper.uid : Process.INVALID_UID;
+    }
+
+    public String getPackageName() {
+        return mHelper != null ? mHelper.packageName : null;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 83a6973..3f322d6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -29,6 +30,8 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * Helper class for managing settings preferences that can be disabled
  * by device admins via user restrictions.
@@ -36,16 +39,22 @@
 public class RestrictedPreferenceHelper {
     private final Context mContext;
     private final Preference mPreference;
+    final String packageName;
+    final int uid;
 
     private boolean mDisabledByAdmin;
     private EnforcedAdmin mEnforcedAdmin;
     private String mAttrUserRestriction = null;
-    private boolean mUseAdminDisabledSummary = false;
+    private boolean mDisabledSummary = false;
+
+    private boolean mDisabledByAppOps;
 
     public RestrictedPreferenceHelper(Context context, Preference preference,
-            AttributeSet attrs) {
+            AttributeSet attrs, String packageName, int uid) {
         mContext = context;
         mPreference = preference;
+        this.packageName = packageName;
+        this.uid = uid;
 
         if (attrs != null) {
             final TypedArray attributes = context.obtainStyledAttributes(attrs,
@@ -71,27 +80,34 @@
             final TypedValue useAdminDisabledSummary =
                     attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary);
             if (useAdminDisabledSummary != null) {
-                mUseAdminDisabledSummary =
+                mDisabledSummary =
                         (useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN
                                 && useAdminDisabledSummary.data != 0);
             }
         }
     }
 
+    public RestrictedPreferenceHelper(Context context, Preference preference,
+            AttributeSet attrs) {
+        this(context, preference, attrs, null, android.os.Process.INVALID_UID);
+    }
+
     /**
      * Modify PreferenceViewHolder to add padlock if restriction is disabled.
      */
     public void onBindViewHolder(PreferenceViewHolder holder) {
-        if (mDisabledByAdmin) {
+        if (mDisabledByAdmin || mDisabledByAppOps) {
             holder.itemView.setEnabled(true);
         }
-        if (mUseAdminDisabledSummary) {
+        if (mDisabledSummary) {
             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
             if (summaryView != null) {
                 final CharSequence disabledText = summaryView.getContext().getText(
                         R.string.disabled_by_admin_summary_text);
                 if (mDisabledByAdmin) {
                     summaryView.setText(disabledText);
+                } else if (mDisabledByAppOps) {
+                    summaryView.setText(R.string.disabled_by_app_ops_text);
                 } else if (TextUtils.equals(disabledText, summaryView.getText())) {
                     // It's previously set to disabled text, clear it.
                     summaryView.setText(null);
@@ -101,7 +117,7 @@
     }
 
     public void useAdminDisabledSummary(boolean useSummary) {
-        mUseAdminDisabledSummary = useSummary;
+        mDisabledSummary = useSummary;
     }
 
     /**
@@ -109,11 +125,19 @@
      *
      * @return true if the method handled the click.
      */
+    @SuppressWarnings("NewApi")
     public boolean performClick() {
         if (mDisabledByAdmin) {
             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
             return true;
         }
+        if (mDisabledByAppOps) {
+            Preconditions.checkState(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU,
+                    "Build SDK version needs >= T");
+            RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
+                    uid);
+            return true;
+        }
         return false;
     }
 
@@ -166,14 +190,34 @@
             changed = true;
         }
 
-        if (!(mPreference instanceof RestrictedTopLevelPreference)) {
-            mPreference.setEnabled(!disabled);
+        updateDisabledState();
+
+        return changed;
+    }
+
+    public boolean setDisabledByAppOps(boolean disabled) {
+        boolean changed = false;
+        if (mDisabledByAppOps != disabled) {
+            mDisabledByAppOps = disabled;
+            changed = true;
         }
 
+        updateDisabledState();
+
         return changed;
     }
 
     public boolean isDisabledByAdmin() {
         return mDisabledByAdmin;
     }
+
+    public boolean isDisabledByAppOps() {
+        return mDisabledByAppOps;
+    }
+
+    private void updateDisabledState() {
+        if (!(mPreference instanceof RestrictedTopLevelPreference)) {
+            mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
new file mode 100644
index 0000000..9dfc8ea
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.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.settingslib.applications;
+
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.LruCache;
+
+/**
+ * Cache app icon for management.
+ */
+public class AppIconCacheManager {
+    private static final String TAG = "AppIconCacheManager";
+    private static final float CACHE_RATIO = 0.1f;
+    private static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
+    private static final String DELIMITER = ":";
+    private static AppIconCacheManager sAppIconCacheManager;
+    private final LruCache<String, Drawable> mDrawableCache;
+
+    private AppIconCacheManager() {
+        mDrawableCache = new LruCache<String, Drawable>(MAX_CACHE_SIZE_IN_KB) {
+            @Override
+            protected int sizeOf(String key, Drawable drawable) {
+                if (drawable instanceof BitmapDrawable) {
+                    return ((BitmapDrawable) drawable).getBitmap().getByteCount() / 1024;
+                }
+                // Rough estimate each pixel will use 4 bytes by default.
+                return drawable.getIntrinsicHeight() * drawable.getIntrinsicWidth() * 4 / 1024;
+            }
+        };
+    }
+
+    /**
+     * Get an {@link AppIconCacheManager} instance.
+     */
+    public static synchronized AppIconCacheManager getInstance() {
+        if (sAppIconCacheManager == null) {
+            sAppIconCacheManager = new AppIconCacheManager();
+        }
+        return sAppIconCacheManager;
+    }
+
+    /**
+     * Put app icon to cache
+     *
+     * @param packageName of icon
+     * @param uid         of packageName
+     * @param drawable    app icon
+     */
+    public void put(String packageName, int uid, Drawable drawable) {
+        final String key = getKey(packageName, uid);
+        if (key == null || drawable == null || drawable.getIntrinsicHeight() < 0
+                || drawable.getIntrinsicWidth() < 0) {
+            Log.w(TAG, "Invalid key or drawable.");
+            return;
+        }
+        mDrawableCache.put(key, drawable);
+    }
+
+    /**
+     * Get app icon from cache.
+     *
+     * @param packageName of icon
+     * @param uid         of packageName
+     * @return app icon
+     */
+    public Drawable get(String packageName, int uid) {
+        final String key = getKey(packageName, uid);
+        if (key == null) {
+            Log.w(TAG, "Invalid key with package or uid.");
+            return null;
+        }
+        final Drawable cachedDrawable = mDrawableCache.get(key);
+        return cachedDrawable != null ? cachedDrawable.mutate() : null;
+    }
+
+    /**
+     * Release cache.
+     */
+    public static void release() {
+        if (sAppIconCacheManager != null) {
+            sAppIconCacheManager.mDrawableCache.evictAll();
+        }
+    }
+
+    private static String getKey(String packageName, int uid) {
+        if (packageName == null || uid < 0) {
+            return null;
+        }
+        return packageName + DELIMITER + UserHandle.getUserId(uid);
+    }
+
+    private static int getMaxCacheInKb() {
+        return Math.round(CACHE_RATIO * Runtime.getRuntime().maxMemory() / 1024);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index a5da8b6..cc4fef8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
 import android.hardware.usb.IUsbManager;
 import android.net.Uri;
 import android.os.Environment;
@@ -35,7 +36,9 @@
 import android.util.Log;
 
 import com.android.settingslib.R;
+import com.android.settingslib.Utils;
 import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -212,4 +215,82 @@
                         UserHandle.myUserId());
         return TextUtils.equals(packageName, defaultBrowserPackage);
     }
+
+    /**
+     * Get the app icon by app entry.
+     *
+     * @param context  caller's context
+     * @param appEntry AppEntry of ApplicationsState
+     * @return app icon of the app entry
+     */
+    public static Drawable getIcon(Context context, ApplicationsState.AppEntry appEntry) {
+        if (appEntry == null || appEntry.info == null) {
+            return null;
+        }
+
+        final AppIconCacheManager appIconCacheManager = AppIconCacheManager.getInstance();
+        final String packageName = appEntry.info.packageName;
+        final int uid = appEntry.info.uid;
+
+        Drawable icon = appIconCacheManager.get(packageName, uid);
+        if (icon == null) {
+            if (appEntry.apkFile != null && appEntry.apkFile.exists()) {
+                icon = Utils.getBadgedIcon(context, appEntry.info);
+                appIconCacheManager.put(packageName, uid, icon);
+            } else {
+                setAppEntryMounted(appEntry, /* mounted= */ false);
+                icon = context.getDrawable(
+                        com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+            }
+        } else if (!appEntry.mounted && appEntry.apkFile != null && appEntry.apkFile.exists()) {
+            // If the app wasn't mounted but is now mounted, reload its icon.
+            setAppEntryMounted(appEntry, /* mounted= */ true);
+            icon = Utils.getBadgedIcon(context, appEntry.info);
+            appIconCacheManager.put(packageName, uid, icon);
+        }
+
+        return icon;
+    }
+
+    /**
+     * Get the app icon from cache by app entry.
+     *
+     * @param appEntry AppEntry of ApplicationsState
+     * @return app icon of the app entry
+     */
+    public static Drawable getIconFromCache(ApplicationsState.AppEntry appEntry) {
+        return appEntry == null || appEntry.info == null ? null
+                : AppIconCacheManager.getInstance().get(
+                        appEntry.info.packageName,
+                        appEntry.info.uid);
+    }
+
+    /**
+     * Preload the top N icons of app entry list.
+     *
+     * @param context caller's context
+     * @param appEntries AppEntry list of ApplicationsState
+     * @param number the number of Top N icons of the appEntries
+     */
+    public static void preloadTopIcons(Context context,
+            ArrayList<ApplicationsState.AppEntry> appEntries, int number) {
+        if (appEntries == null || appEntries.isEmpty() || number <= 0) {
+            return;
+        }
+
+        for (int i = 0; i < Math.min(appEntries.size(), number); i++) {
+            final ApplicationsState.AppEntry entry = appEntries.get(i);
+            ThreadUtils.postOnBackgroundThread(() -> {
+                getIcon(context, entry);
+            });
+        }
+    }
+
+    private static void setAppEntryMounted(ApplicationsState.AppEntry appEntry, boolean mounted) {
+        if (appEntry.mounted != mounted) {
+            synchronized (appEntry) {
+                appEntry.mounted = mounted;
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index f046f06..fdb0607 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -95,6 +95,7 @@
     private static final Object sLock = new Object();
     private static final Pattern REMOVE_DIACRITICALS_PATTERN
             = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+    private static final String SETTING_PKG = "com.android.settings";
 
     @VisibleForTesting
     static ApplicationsState sInstance;
@@ -492,6 +493,9 @@
         return null;
     }
 
+    /**
+     * Starting Android T, this method will not be used if {@link AppIconCacheManager} is applied.
+     */
     public void ensureIcon(AppEntry entry) {
         if (entry.icon != null) {
             return;
@@ -758,6 +762,10 @@
         return null;
     }
 
+    private static boolean isAppIconCacheEnabled(Context context) {
+        return SETTING_PKG.equals(context.getPackageName());
+    }
+
     void rebuildActiveSessions() {
         synchronized (mEntriesMap) {
             if (!mSessionsChanged) {
@@ -806,6 +814,11 @@
             } else {
                 mHasLifecycle = false;
             }
+
+            if (isAppIconCacheEnabled(mContext)) {
+                // Skip the preloading all icons step to save memory usage.
+                mFlags = mFlags & ~FLAG_SESSION_REQUEST_ICONS;
+            }
         }
 
         @SessionFlags
@@ -814,7 +827,12 @@
         }
 
         public void setSessionFlags(@SessionFlags int flags) {
-            mFlags = flags;
+            if (isAppIconCacheEnabled(mContext)) {
+                // Skip the preloading all icons step to save memory usage.
+                mFlags = flags & ~FLAG_SESSION_REQUEST_ICONS;
+            } else {
+                mFlags = flags;
+            }
         }
 
         @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -1576,6 +1594,10 @@
 
         // Need to synchronize on 'this' for the following.
         public ApplicationInfo info;
+        /**
+         * Starting Android T, this field will not be used if {@link AppIconCacheManager} is
+         * applied.
+         */
         public Drawable icon;
         public String sizeStr;
         public String internalSizeStr;
@@ -1596,15 +1618,11 @@
             this.size = SIZE_UNKNOWN;
             this.sizeStale = true;
             ensureLabel(context);
-            // Speed up the cache of the icon and label description if they haven't been created.
-            ThreadUtils.postOnBackgroundThread(() -> {
-                if (this.icon == null) {
-                    this.ensureIconLocked(context);
-                }
-                if (this.labelDescription == null) {
-                    this.ensureLabelDescriptionLocked(context);
-                }
-            });
+            // Speed up the cache of the label description if they haven't been created.
+            if (this.labelDescription == null) {
+                ThreadUtils.postOnBackgroundThread(
+                        () -> this.ensureLabelDescriptionLocked(context));
+            }
         }
 
         public void ensureLabel(Context context) {
@@ -1620,7 +1638,15 @@
             }
         }
 
+        /**
+         * Starting Android T, this method will not be used if {@link AppIconCacheManager} is
+         * applied.
+         */
         boolean ensureIconLocked(Context context) {
+            if (isAppIconCacheEnabled(context)) {
+                return false;
+            }
+
             if (this.icon == null) {
                 if (this.apkFile.exists()) {
                     this.icon = Utils.getBadgedIcon(context, info);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
rename to packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index bec5fc8..afd3626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.policy;
+package com.android.settingslib.devicestate;
 
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 7168f3c..46e31ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -116,6 +116,7 @@
     private final boolean mDreamsEnabledByDefault;
     private final boolean mDreamsActivatedOnSleepByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
+    private final Set<ComponentName> mDisabledDreams;
     private final Set<Integer> mSupportedComplications;
     private final Set<Integer> mDefaultEnabledComplications;
 
@@ -143,6 +144,10 @@
                 com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
         mDreamPreviewDefault = resources.getDrawable(
                 com.android.internal.R.drawable.default_dream_preview);
+        mDisabledDreams = Arrays.stream(resources.getStringArray(
+                        com.android.internal.R.array.config_disabledDreamComponents))
+                .map(ComponentName::unflattenFromString)
+                .collect(Collectors.toSet());
 
         mSupportedComplications =
                 Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
@@ -166,14 +171,16 @@
                 PackageManager.GET_META_DATA);
         List<DreamInfo> dreamInfos = new ArrayList<>(resolveInfos.size());
         for (ResolveInfo resolveInfo : resolveInfos) {
-            if (resolveInfo.serviceInfo == null) {
+            final ComponentName componentName = getDreamComponentName(resolveInfo);
+            if (componentName == null || mDisabledDreams.contains(componentName)) {
                 continue;
             }
+
             DreamInfo dreamInfo = new DreamInfo();
             dreamInfo.caption = resolveInfo.loadLabel(pm);
             dreamInfo.icon = resolveInfo.loadIcon(pm);
             dreamInfo.description = getDescription(resolveInfo, pm);
-            dreamInfo.componentName = getDreamComponentName(resolveInfo);
+            dreamInfo.componentName = componentName;
             dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
 
             final DreamMetadata dreamMetadata = getDreamMetadata(pm, resolveInfo);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index 5859953..6204336 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -143,9 +143,8 @@
             mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter,
                     userPhotoView);
         } else {
-            // some users can't change their photos so we need to remove suggestive
-            // background from the photoView
-            userPhotoView.setBackground(null);
+            // some users can't change their photos, so we need to remove the suggestive icon
+            content.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE);
         }
 
         mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
new file mode 100644
index 0000000..64f8bef
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.graphics.drawable.Drawable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppIconCacheManagerTest {
+
+    private static final String APP_PACKAGE_NAME = "com.test.app";
+    private static final int APP_UID = 9999;
+
+    @Mock
+    private Drawable mIcon;
+
+    private AppIconCacheManager mAppIconCacheManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mAppIconCacheManager = AppIconCacheManager.getInstance();
+        doReturn(10).when(mIcon).getIntrinsicHeight();
+        doReturn(10).when(mIcon).getIntrinsicWidth();
+        doReturn(mIcon).when(mIcon).mutate();
+    }
+
+    @After
+    public void tearDown() {
+        AppIconCacheManager.release();
+    }
+
+    @Test
+    public void get_invalidPackageOrUid_shouldReturnNull() {
+        assertThat(mAppIconCacheManager.get(/* packageName= */ null, /* uid= */ -1)).isNull();
+    }
+
+    @Test
+    public void put_invalidPackageOrUid_shouldNotCrash() {
+        mAppIconCacheManager.put(/* packageName= */ null, /* uid= */ 0, mIcon);
+        // no crash
+    }
+
+    @Test
+    public void put_invalidIcon_shouldNotCacheIcon() {
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, /* drawable= */ null);
+
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+    }
+
+    @Test
+    public void put_invalidIconSize_shouldNotCacheIcon() {
+        doReturn(-1).when(mIcon).getIntrinsicHeight();
+        doReturn(-1).when(mIcon).getIntrinsicWidth();
+
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+    }
+
+    @Test
+    public void put_shouldCacheIcon() {
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isEqualTo(mIcon);
+    }
+
+    @Test
+    public void release_noInstance_shouldNotCrash() {
+        mAppIconCacheManager = null;
+
+        AppIconCacheManager.release();
+        // no crash
+    }
+
+    @Test
+    public void release_existInstance_shouldClearCache() {
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+        AppIconCacheManager.release();
+
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
new file mode 100644
index 0000000..8e448aa
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppUtilsTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppUtilsTest {
+
+    private static final String APP_PACKAGE_NAME = "com.test.app";
+    private static final int APP_UID = 9999;
+
+    @Mock
+    private Drawable mIcon;
+
+    private Context mContext;
+    private AppIconCacheManager mAppIconCacheManager;
+    private ApplicationInfo mAppInfo;
+    private ApplicationsState.AppEntry mAppEntry;
+    private ArrayList<ApplicationsState.AppEntry> mAppEntries;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mAppIconCacheManager = AppIconCacheManager.getInstance();
+        mAppInfo = createApplicationInfo(APP_PACKAGE_NAME, APP_UID);
+        mAppEntry = createAppEntry(mAppInfo, /* id= */ 1);
+        mAppEntries = new ArrayList<>(Arrays.asList(mAppEntry));
+        doReturn(mIcon).when(mIcon).mutate();
+    }
+
+    @After
+    public void tearDown() {
+        AppIconCacheManager.release();
+    }
+
+    @Test
+    public void getIcon_nullAppEntry_shouldReturnNull() {
+        assertThat(AppUtils.getIcon(mContext, /* appEntry= */ null)).isNull();
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void getIcon_noCachedIcon_shouldNotReturnNull() {
+        assertThat(AppUtils.getIcon(mContext, mAppEntry)).isNotNull();
+    }
+
+    @Test
+    public void getIcon_existCachedIcon_shouldReturnCachedIcon() {
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+        assertThat(AppUtils.getIcon(mContext, mAppEntry)).isEqualTo(mIcon);
+    }
+
+    @Test
+    public void getIconFromCache_nullAppEntry_shouldReturnNull() {
+        assertThat(AppUtils.getIconFromCache(/* appEntry= */ null)).isNull();
+    }
+
+    @Test
+    public void getIconFromCache_shouldReturnCachedIcon() {
+        mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
+
+        assertThat(AppUtils.getIconFromCache(mAppEntry)).isEqualTo(mIcon);
+    }
+
+    @Test
+    public void preloadTopIcons_nullAppEntries_shouldNotCrash() {
+        AppUtils.preloadTopIcons(mContext, /* appEntries= */ null, /* number= */ 1);
+        // no crash
+    }
+
+    @Test
+    public void preloadTopIcons_zeroPreloadIcons_shouldNotCacheIcons() {
+        AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 0);
+
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void preloadTopIcons_shouldCheckIconFromCache() throws InterruptedException {
+        AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 1);
+
+        TimeUnit.SECONDS.sleep(1);
+        assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNotNull();
+    }
+
+    private ApplicationsState.AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
+        ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo, id);
+        appEntry.label = "label";
+        appEntry.mounted = true;
+        final File apkFile = mock(File.class);
+        doReturn(true).when(apkFile).exists();
+        try {
+            Field field = ApplicationsState.AppEntry.class.getDeclaredField("apkFile");
+            field.setAccessible(true);
+            field.set(appEntry, apkFile);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            fail("Not able to mock apkFile: " + e);
+        }
+        return appEntry;
+    }
+
+    private ApplicationInfo createApplicationInfo(String packageName, int uid) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.sourceDir = "appPath";
+        appInfo.packageName = packageName;
+        appInfo.uid = uid;
+        return appInfo;
+    }
+
+    @Implements(Utils.class)
+    private static class ShadowUtils {
+        @Implementation
+        public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) {
+            final Drawable icon = mock(Drawable.class);
+            doReturn(10).when(icon).getIntrinsicHeight();
+            doReturn(10).when(icon).getIntrinsicWidth();
+            doReturn(icon).when(icon).mutate();
+            return icon;
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 10ccd22..1f2297b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -33,6 +33,7 @@
 import static org.robolectric.shadow.api.Shadow.extract;
 
 import android.annotation.UserIdInt;
+import android.app.Application;
 import android.app.ApplicationPackageManager;
 import android.app.usage.StorageStats;
 import android.app.usage.StorageStatsManager;
@@ -110,6 +111,7 @@
     private ApplicationsState mApplicationsState;
     private Session mSession;
 
+    private Application mApplication;
 
     @Mock
     private Callbacks mCallbacks;
@@ -190,6 +192,7 @@
         ShadowContextImpl shadowContext = Shadow.extract(
                 RuntimeEnvironment.application.getBaseContext());
         shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
+        mApplication = spy(RuntimeEnvironment.application);
         StorageStats storageStats = new StorageStats();
         storageStats.codeBytes = 10;
         storageStats.cacheBytes = 30;
@@ -207,8 +210,7 @@
             anyLong() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos));
 
         ApplicationsState.sInstance = null;
-        mApplicationsState =
-            ApplicationsState.getInstance(RuntimeEnvironment.application, mPackageManagerService);
+        mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
         mApplicationsState.clearEntries();
 
         mSession = mApplicationsState.newSession(mCallbacks);
@@ -703,6 +705,23 @@
         verify(mApplicationsState, never()).clearEntries();
     }
 
+    @Test
+    public void testDefaultSession_enabledAppIconCache_shouldSkipPreloadIcon() {
+        when(mApplication.getPackageName()).thenReturn("com.android.settings");
+        mSession.onResume();
+
+        addApp(HOME_PACKAGE_NAME, 1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 2);
+        mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+        verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
+
+        List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
+        for (AppEntry appEntry : appEntries) {
+            assertThat(appEntry.icon).isNull();
+        }
+    }
+
     private void setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps,
             ArrayList<ApplicationInfo> profileApps)
             throws RemoteException {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index c1cc3ae..445701f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doReturn;
@@ -33,7 +34,6 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -55,7 +55,6 @@
     }
 
     @Test
-    @Ignore
     public void testBroadcastReceiver() {
         final AbstractConnectivityPreferenceController preferenceController =
                 spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
@@ -73,7 +72,7 @@
         verify(mContext, times(1))
                 .registerReceiver(receiverArgumentCaptor.capture(),
                         filterArgumentCaptor.capture(),
-                        anyString(), nullable(Handler.class));
+                        anyString(), nullable(Handler.class), anyInt());
 
         final BroadcastReceiver receiver = receiverArgumentCaptor.getValue();
         final IntentFilter filter = filterArgumentCaptor.getValue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 5b0f659..2f3da83 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.net;
 
+import static android.app.usage.NetworkStats.Bucket.STATE_ALL;
 import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
 
@@ -83,7 +84,6 @@
 
         mLoader.recordUsage(start, end);
 
-        verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, uid);
         verify(mNetworkStatsManager).queryDetailsForUidTagState(
                 mNetworkTemplate, start, end, uid, TAG_NONE, STATE_FOREGROUND);
     }
@@ -116,9 +116,12 @@
 
         mLoader.recordUsage(start, end);
 
-        verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 1);
-        verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 2);
-        verify(mNetworkStatsManager).queryDetailsForUid(mNetworkTemplate, start, end, 3);
+        verify(mNetworkStatsManager).queryDetailsForUidTagState(mNetworkTemplate, start, end, 1,
+                TAG_NONE, STATE_ALL);
+        verify(mNetworkStatsManager).queryDetailsForUidTagState(mNetworkTemplate, start, end, 2,
+                TAG_NONE, STATE_ALL);
+        verify(mNetworkStatsManager).queryDetailsForUidTagState(mNetworkTemplate, start, end, 3,
+                TAG_NONE, STATE_ALL);
     }
 
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 10ac829..3b18c57 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -64,6 +64,20 @@
     }
 
     @Test
+    public void setLearnMoreText_shouldSetAsTextInLearnMore() {
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null));
+        mFooterPreference.setLearnMoreText("Custom learn more");
+        mFooterPreference.setLearnMoreAction(view -> { /* do nothing */ } /* listener */);
+
+        mFooterPreference.onBindViewHolder(holder);
+
+        assertThat(((TextView) holder.findViewById(
+                        R.id.settingslib_learn_more)).getText().toString())
+                .isEqualTo("Custom learn more");
+    }
+
+    @Test
     public void setContentDescription_contentSet_shouldGetSameContentDescription() {
         mFooterPreference.setContentDescription("test");
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 927f11f..51870e2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3624,7 +3624,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 208;
+            private static final int SETTINGS_VERSION = 209;
 
             private final int mUserId;
 
@@ -5497,6 +5497,20 @@
                     currentVersion = 208;
                 }
 
+                if (currentVersion == 208) {
+                    // Version 208: Enable enforcement of
+                    // android.Manifest.permission#POST_NOTIFICATIONS in order for applications
+                    // to post notifications.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    secureSettings.insertSettingLocked(
+                            Secure.NOTIFICATION_PERMISSION_ENABLED,
+                            /* enabled= */" 1",
+                            /* tag= */ null,
+                            /* makeDefault= */ false,
+                            SettingsState.SYSTEM_PACKAGE_NAME);
+                    currentVersion = 209;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 52a708d..13ae870 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -326,6 +326,8 @@
                     Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
                     Settings.Global.LOW_POWER_MODE_STICKY,
                     Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS,
+                    Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+                    Settings.Global.LOW_POWER_STANDBY_ENABLED,
                     Settings.Global.LTE_SERVICE_FORCED,
                     Settings.Global.LID_BEHAVIOR,
                     Settings.Global.MAX_ERROR_BYTES_PREFIX,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4dddc8c..27fc6ba 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -204,7 +204,6 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.CREATE_USERS" />
     <uses-permission android:name="android.permission.QUERY_USERS" />
-    <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
     <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
@@ -249,6 +248,7 @@
     <uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
     <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" />
     <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
+    <uses-permission android:name="android.permission.MANAGE_LOW_POWER_STANDBY" />
     <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
     <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
     <uses-permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" />
@@ -562,6 +562,9 @@
     <!-- Permission required for CTS test - CtsGameManagerTestCases -->
     <uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
 
+    <!-- Permission required for CTS test - CtsGameServiceTestCases -->
+    <uses-permission android:name="android.permission.SET_GAME_SERVICE" />
+
     <!-- Permission required for CTS test - ClipboardManagerTest -->
     <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
 
@@ -630,6 +633,11 @@
     <uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
     <uses-permission android:name="android.permission.MANAGE_SAFETY_CENTER" />
 
+    <!-- Permissions required for CTS test - CtsVirtualDevicesTestCases -->
+    <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
+    <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
+    <uses-permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" />
+
 
     <!-- Permission required for CTS test - Notification test suite -->
     <uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 137a1fd..b1cfb11 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -107,6 +107,7 @@
         "androidx.slice_slice-view",
         "androidx.slice_slice-builders",
         "androidx.arch.core_core-runtime",
+        "androidx.lifecycle_lifecycle-common-java8",
         "androidx.lifecycle_lifecycle-extensions",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
@@ -209,6 +210,7 @@
         "androidx.slice_slice-view",
         "androidx.slice_slice-builders",
         "androidx.arch.core_core-runtime",
+        "androidx.lifecycle_lifecycle-common-java8",
         "androidx.lifecycle_lifecycle-extensions",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 68c8c3e..ffee894 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,6 +20,7 @@
 import android.app.smartspace.SmartspaceAction;
 import android.app.smartspace.SmartspaceTarget;
 import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.uitemplatedata.SmartspaceTapAction;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
@@ -143,6 +144,18 @@
             }
         }
 
+        default void startFromAction(SmartspaceTapAction action, View v, boolean showOnLockscreen) {
+            try {
+                if (action.getIntent() != null) {
+                    startIntent(v, action.getIntent(), showOnLockscreen);
+                } else if (action.getPendingIntent() != null) {
+                    startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+                }
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "Could not launch intent for action: " + action, e);
+            }
+        }
+
         /** Start the intent */
         void startIntent(View v, Intent i, boolean showOnLockscreen);
 
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 7ffb3b2..2a3761e 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -17,6 +17,7 @@
 <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:alpha="0"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <ImageView
diff --git a/packages/SystemUI/res/layout/privacy_dot_bottom_left.xml b/packages/SystemUI/res/layout/privacy_dot_bottom_left.xml
new file mode 100644
index 0000000..328570b
--- /dev/null
+++ b/packages/SystemUI/res/layout/privacy_dot_bottom_left.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_dot_bottom_left_container"
+    android:layout_height="@dimen/status_bar_height"
+    android:layout_width="wrap_content"
+    android:layout_gravity="bottom|left"
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:visibility="invisible" >
+    <ImageView
+        android:id="@+id/privacy_dot"
+        android:contentDescription="@null"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical|right"
+        android:src="@drawable/system_animation_ongoing_dot"
+        android:visibility="visible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dot_bottom_right.xml b/packages/SystemUI/res/layout/privacy_dot_bottom_right.xml
new file mode 100644
index 0000000..34b74f3
--- /dev/null
+++ b/packages/SystemUI/res/layout/privacy_dot_bottom_right.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_dot_bottom_right_container"
+    android:layout_height="@dimen/status_bar_height"
+    android:layout_width="wrap_content"
+    android:layout_gravity="bottom|right"
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:visibility="invisible" >
+    <ImageView
+        android:id="@+id/privacy_dot"
+        android:contentDescription="@null"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical|left"
+        android:src="@drawable/system_animation_ongoing_dot"
+        android:visibility="visible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dot_top_left.xml b/packages/SystemUI/res/layout/privacy_dot_top_left.xml
new file mode 100644
index 0000000..ea6c886
--- /dev/null
+++ b/packages/SystemUI/res/layout/privacy_dot_top_left.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_dot_top_left_container"
+    android:layout_height="@dimen/status_bar_height"
+    android:layout_width="wrap_content"
+    android:layout_gravity="top|left"
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:visibility="invisible" >
+    <ImageView
+        android:id="@+id/privacy_dot"
+        android:contentDescription="@null"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical|right"
+        android:src="@drawable/system_animation_ongoing_dot"
+        android:visibility="visible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dot_top_right.xml b/packages/SystemUI/res/layout/privacy_dot_top_right.xml
new file mode 100644
index 0000000..bcda0da
--- /dev/null
+++ b/packages/SystemUI/res/layout/privacy_dot_top_right.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_dot_top_right_container"
+    android:layout_height="@dimen/status_bar_height"
+    android:layout_width="wrap_content"
+    android:layout_gravity="top|right"
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:visibility="invisible" >
+    <ImageView
+        android:id="@+id/privacy_dot"
+        android:contentDescription="@null"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical|left"
+        android:src="@drawable/system_animation_ongoing_dot"
+        android:visibility="visible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index f91ab6f..b2857ab 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -27,22 +27,6 @@
         android:tint="#ff000000"
         android:src="@drawable/rounded_corner_bottom"/>
 
-    <FrameLayout
-        android:id="@+id/privacy_dot_left_container"
-        android:layout_height="@dimen/status_bar_height"
-        android:layout_width="wrap_content"
-        android:paddingTop="@dimen/status_bar_padding_top"
-        android:layout_gravity="left|bottom"
-        android:visibility="invisible" >
-        <ImageView
-            android:id="@+id/privacy_dot"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|right"
-            android:src="@drawable/system_animation_ongoing_dot"
-            android:visibility="visible" />
-    </FrameLayout>
-
     <ImageView
         android:id="@+id/right"
         android:layout_width="12dp"
@@ -51,20 +35,4 @@
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded_corner_bottom"/>
 
-    <FrameLayout
-        android:id="@+id/privacy_dot_right_container"
-        android:layout_height="@dimen/status_bar_height"
-        android:layout_width="wrap_content"
-        android:paddingTop="@dimen/status_bar_padding_top"
-        android:layout_gravity="right|bottom"
-        android:visibility="invisible" >
-        <ImageView
-            android:id="@+id/privacy_dot"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|left"
-            android:src="@drawable/system_animation_ongoing_dot"
-            android:visibility="visible" />
-    </FrameLayout>
-
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 819a9a4e9..9937c21 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -27,22 +27,6 @@
         android:tint="#ff000000"
         android:src="@drawable/rounded_corner_top"/>
 
-    <FrameLayout
-        android:id="@+id/privacy_dot_left_container"
-        android:layout_height="@dimen/status_bar_height"
-        android:layout_width="wrap_content"
-        android:paddingTop="@dimen/status_bar_padding_top"
-        android:layout_gravity="left|top"
-        android:visibility="invisible" >
-        <ImageView
-            android:id="@+id/privacy_dot"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|right"
-            android:src="@drawable/system_animation_ongoing_dot"
-            android:visibility="visible" />
-    </FrameLayout>
-
     <ImageView
         android:id="@+id/right"
         android:layout_width="12dp"
@@ -51,20 +35,4 @@
         android:layout_gravity="right|top"
         android:src="@drawable/rounded_corner_top"/>
 
-    <FrameLayout
-        android:id="@+id/privacy_dot_right_container"
-        android:layout_height="@dimen/status_bar_height"
-        android:layout_width="wrap_content"
-        android:paddingTop="@dimen/status_bar_padding_top"
-        android:layout_gravity="right|top"
-        android:visibility="invisible" >
-        <ImageView
-            android:id="@+id/privacy_dot"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|left"
-            android:src="@drawable/system_animation_ongoing_dot"
-            android:visibility="visible" />
-    </FrameLayout>
-
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index af7ef53..b35571c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -369,6 +369,13 @@
     <!-- The top margin of the panel that holds the list of notifications. -->
     <dimen name="notification_panel_margin_top">0dp</dimen>
 
+    <!-- The minimum content height for the split shade NSSL.
+         It is used because if the height is too small, the expansion motion is too fast.
+         Note that the value of 256dp is more or less a random value and can be changed to tweak
+         the expansion motion.
+    -->
+    <dimen name="nssl_split_shade_min_content_height">256dp</dimen>
+
     <!-- The bottom margin of the panel that holds the list of notifications. -->
     <dimen name="notification_panel_margin_bottom">0dp</dimen>
 
@@ -432,7 +439,9 @@
     <!-- The maximum width of the navigation bar ripples. -->
     <dimen name="key_button_ripple_max_width">95dp</dimen>
 
-    <dimen name="rounded_corner_content_padding">0dp</dimen>
+    <dimen name="rounded_corner_content_padding">
+        @*android:dimen/rounded_corner_content_padding
+    </dimen>
 
     <dimen name="navigation_key_padding">0dp</dimen>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 5d092d0..eebc791 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -120,6 +120,8 @@
     public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
     // The bubble stack is expanded AND the mange menu for bubbles is expanded on top of it.
     public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
+    // The current app is in immersive mode
+    public static final int SYSUI_STATE_IMMERSIVE_MODE = 1 << 24;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -145,7 +147,8 @@
             SYSUI_STATE_IME_SWITCHER_SHOWING,
             SYSUI_STATE_DEVICE_DOZING,
             SYSUI_STATE_BACK_DISABLED,
-            SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
+            SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
+            SYSUI_STATE_IMMERSIVE_MODE
     })
     public @interface SystemUiStateFlags {}
 
@@ -179,6 +182,7 @@
         str.add((flags & SYSUI_STATE_BACK_DISABLED) != 0 ? "back_disabled" : "");
         str.add((flags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0
                 ? "bubbles_mange_menu_expanded" : "");
+        str.add((flags & SYSUI_STATE_IMMERSIVE_MODE) != 0 ? "immersive_mode" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
index 214b284..43cd764 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard
 
+import android.app.StatusBarManager.SESSION_KEYGUARD
 import android.content.Context
 import android.hardware.biometrics.BiometricSourceType
 import com.android.internal.annotations.VisibleForTesting
@@ -28,7 +29,7 @@
 import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.SessionTracker
 import java.io.FileDescriptor
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -44,7 +45,7 @@
     context: Context?,
     private val uiEventLogger: UiEventLogger,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val dumpManager: DumpManager
+    private val sessionTracker: SessionTracker
 ) : CoreStartable(context) {
     private var fingerprintLockedOut = false
     private var faceLockedOut = false
@@ -53,7 +54,6 @@
     private var timeout = false
 
     override fun start() {
-        dumpManager.registerDumpable(this)
         mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
                 KeyguardUpdateMonitor.getCurrentUser())
         keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
@@ -65,22 +65,17 @@
             if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                 val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
                 if (lockedOut && !fingerprintLockedOut) {
-                    uiEventLogger.log(
-                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+                    log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
                 } else if (!lockedOut && fingerprintLockedOut) {
-                    uiEventLogger.log(
-                            PrimaryAuthRequiredEvent
-                                    .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+                    log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
                 }
                 fingerprintLockedOut = lockedOut
             } else if (biometricSourceType == BiometricSourceType.FACE) {
                 val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
                 if (lockedOut && !faceLockedOut) {
-                    uiEventLogger.log(
-                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+                    log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
                 } else if (!lockedOut && faceLockedOut) {
-                    uiEventLogger.log(
-                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+                    log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
                 }
                 faceLockedOut = lockedOut
             }
@@ -95,20 +90,19 @@
 
             val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
             if (newEncryptedOrLockdown && !encryptedOrLockdown) {
-                uiEventLogger.log(
-                        PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+                log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
             }
             encryptedOrLockdown = newEncryptedOrLockdown
 
             val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
             if (newUnattendedUpdate && !unattendedUpdate) {
-                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+                log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
             }
             unattendedUpdate = newUnattendedUpdate
 
             val newTimeout = isStrongAuthTimeout(strongAuthFlags)
             if (newTimeout && !timeout) {
-                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
+                log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
             }
             timeout = newTimeout
         }
@@ -123,6 +117,9 @@
     ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
             containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
 
+    private fun log(event: PrimaryAuthRequiredEvent) =
+            uiEventLogger.log(event, sessionTracker.getSessionId(SESSION_KEYGUARD))
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
         pw.println("  mFingerprintLockedOut=$fingerprintLockedOut")
         pw.println("  mFaceLockedOut=$faceLockedOut")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 3fab724..c387260 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -20,6 +20,8 @@
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
 
 import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
 
 import static java.lang.Integer.max;
 
@@ -892,6 +894,9 @@
                     } else {
                         textView.setBackground(null);
                     }
+                    view.setEnabled(item.isSwitchToEnabled);
+                    view.setAlpha(view.isEnabled() ? USER_SWITCH_ENABLED_ALPHA :
+                            USER_SWITCH_DISABLED_ALPHA);
                     return view;
                 }
 
@@ -941,6 +946,7 @@
                 mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                         public void onItemClick(AdapterView parent, View view, int pos, long id) {
                             if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
+                            if (!view.isEnabled()) return;
 
                             // Subtract one for the header
                             UserRecord user = adapter.getItem(pos - 1);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 49a8022..57997d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
+
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
@@ -36,7 +38,10 @@
 import android.util.Slog;
 import android.view.MotionEvent;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -53,6 +58,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -86,6 +92,7 @@
     private final UserSwitcherController mUserSwitcherController;
     private final GlobalSettings mGlobalSettings;
     private final FeatureFlags mFeatureFlags;
+    private final SessionTracker mSessionTracker;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
@@ -191,7 +198,7 @@
             mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
                     .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
             mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
-                    : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+                            : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
         }
 
         public void reset() {
@@ -242,7 +249,8 @@
             FalsingManager falsingManager,
             UserSwitcherController userSwitcherController,
             FeatureFlags featureFlags,
-            GlobalSettings globalSettings) {
+            GlobalSettings globalSettings,
+            SessionTracker sessionTracker) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -261,6 +269,7 @@
         mUserSwitcherController = userSwitcherController;
         mFeatureFlags = featureFlags;
         mGlobalSettings = globalSettings;
+        mSessionTracker = sessionTracker;
     }
 
     @Override
@@ -456,7 +465,7 @@
                     .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
         }
         if (uiEvent != BouncerUiEvent.UNKNOWN) {
-            mUiEventLogger.log(uiEvent);
+            mUiEventLogger.log(uiEvent, getSessionId());
         }
         if (finish) {
             mSecurityCallback.finish(strongAuth, targetUserId);
@@ -599,6 +608,10 @@
         }
     }
 
+    private @Nullable InstanceId getSessionId() {
+        return mSessionTracker.getSessionId(SESSION_KEYGUARD);
+    }
+
     /** Update keyguard position based on a tapped X coordinate. */
     public void updateKeyguardPosition(float x) {
         mView.updatePositionByTouchX(x);
@@ -622,6 +635,7 @@
         private final GlobalSettings mGlobalSettings;
         private final FeatureFlags mFeatureFlags;
         private final UserSwitcherController mUserSwitcherController;
+        private final SessionTracker mSessionTracker;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -639,7 +653,8 @@
                 FalsingManager falsingManager,
                 UserSwitcherController userSwitcherController,
                 FeatureFlags featureFlags,
-                GlobalSettings globalSettings) {
+                GlobalSettings globalSettings,
+                SessionTracker sessionTracker) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
             mLockPatternUtils = lockPatternUtils;
@@ -655,6 +670,7 @@
             mFeatureFlags = featureFlags;
             mGlobalSettings = globalSettings;
             mUserSwitcherController = userSwitcherController;
+            mSessionTracker = sessionTracker;
         }
 
         public KeyguardSecurityContainerController create(
@@ -664,7 +680,7 @@
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
                     mConfigurationController, mFalsingCollector, mFalsingManager,
-                    mUserSwitcherController, mFeatureFlags, mGlobalSettings);
+                    mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 6626f59..80a3a0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,7 +35,6 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Process;
 import android.os.VibrationAttributes;
-import android.os.Vibrator;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
@@ -60,6 +59,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -103,7 +103,7 @@
 
     @NonNull private CharSequence mUnlockedLabel;
     @NonNull private CharSequence mLockedLabel;
-    @Nullable private final Vibrator mVibrator;
+    @NonNull private final VibratorHelper mVibrator;
     @Nullable private final AuthRippleController mAuthRippleController;
 
     // Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -154,7 +154,7 @@
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull ConfigurationController configurationController,
             @NonNull @Main DelayableExecutor executor,
-            @Nullable Vibrator vibrator,
+            @NonNull VibratorHelper vibrator,
             @Nullable AuthRippleController authRippleController,
             @NonNull @Main Resources resources
     ) {
@@ -560,7 +560,7 @@
         switch(event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_HOVER_ENTER:
-                if (mVibrator != null && !mDownDetected) {
+                if (!mDownDetected) {
                     mVibrator.vibrate(
                             Process.myUid(),
                             getContext().getOpPackageName(),
@@ -647,15 +647,13 @@
             mOnGestureDetectedRunnable.run();
         }
 
-        if (mVibrator != null) {
-            // play device entry haptic (same as biometric success haptic)
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    getContext().getOpPackageName(),
-                    UdfpsController.EFFECT_CLICK,
-                    "lock-icon-device-entry",
-                    TOUCH_VIBRATION_ATTRIBUTES);
-        }
+        // play device entry haptic (same as biometric success haptic)
+        mVibrator.vibrate(
+                Process.myUid(),
+                getContext().getOpPackageName(),
+                UdfpsController.EFFECT_CLICK,
+                "lock-icon-device-entry",
+                TOUCH_VIBRATION_ATTRIBUTES);
 
         mKeyguardViewController.showBouncer(/* scrim */ true);
     }
@@ -670,12 +668,9 @@
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
-        if (mVibrator != null) {
-            mVibrator.cancel();
-        }
+        mVibrator.cancel();
     }
 
-
     private boolean inLockIconArea(MotionEvent event) {
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                 && mView.getVisibility() == View.VISIBLE;
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 0b967b7..f00615b 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -23,6 +23,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -69,6 +70,19 @@
     // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U
     private static final float STRETCH_INTERVAL = 2f;
 
+    private static final FloatProperty<ViewScaler> VIEW_SCALER_HEIGHT_PROPERTY =
+            new FloatProperty<ViewScaler>("ViewScalerHeight") {
+        @Override
+        public void setValue(ViewScaler object, float value) {
+            object.setHeight(value);
+        }
+
+        @Override
+        public Float get(ViewScaler object) {
+            return object.getHeight();
+        }
+    };
+
     @SuppressWarnings("unused")
     private Context mContext;
 
@@ -147,6 +161,7 @@
         public void setView(ExpandableView v) {
             mView = v;
         }
+
         public void setHeight(float h) {
             if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
             mView.setActualHeight((int) h);
@@ -176,7 +191,7 @@
         mCallback = callback;
         mScaler = new ViewScaler();
         mGravity = Gravity.TOP;
-        mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
+        mScaleAnimation = ObjectAnimator.ofFloat(mScaler, VIEW_SCALER_HEIGHT_PROPERTY, 0f);
         mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
 
         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9c2971c..783415e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -32,6 +32,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Dimension;
+import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -87,6 +88,11 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.decor.DecorProvider;
+import com.android.systemui.decor.DecorProviderFactory;
+import com.android.systemui.decor.DecorProviderKt;
+import com.android.systemui.decor.OverlayWindow;
+import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
 import com.android.systemui.qs.SettingObserver;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
@@ -104,6 +110,8 @@
 
 import javax.inject.Inject;
 
+import kotlin.Pair;
+
 /**
  * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
  * for antialiasing and emulation purposes.
@@ -135,6 +143,7 @@
     private final UserTracker mUserTracker;
     private final PrivacyDotViewController mDotViewController;
     private final ThreadFactory mThreadFactory;
+    private final DecorProviderFactory mDotFactory;
 
     //TODO: These are piecemeal being updated to Points for now to support non-square rounded
     // corners. for now it is only supposed when reading the intrinsic size from the drawables with
@@ -146,14 +155,9 @@
     @VisibleForTesting
     protected Point mRoundedDefaultBottom = new Point(0, 0);
     @VisibleForTesting
-    protected View[] mOverlays;
+    protected OverlayWindow[] mOverlays = null;
     @Nullable
     private DisplayCutoutView[] mCutoutViews;
-    //TODO:
-    View mTopLeftDot;
-    View mTopRightDot;
-    View mBottomLeftDot;
-    View mBottomRightDot;
     private float mDensity;
     private WindowManager mWindowManager;
     private int mRotation;
@@ -162,7 +166,6 @@
     private Handler mHandler;
     private boolean mPendingRotationChange;
     private boolean mIsRoundedCornerMultipleRadius;
-    private boolean mIsPrivacyDotEnabled;
     private Drawable mRoundedCornerDrawable;
     private Drawable mRoundedCornerDrawableTop;
     private Drawable mRoundedCornerDrawableBottom;
@@ -227,7 +230,8 @@
             TunerService tunerService,
             UserTracker userTracker,
             PrivacyDotViewController dotViewController,
-            ThreadFactory threadFactory) {
+            ThreadFactory threadFactory,
+            PrivacyDotDecorProviderFactory dotFactory) {
         super(context);
         mMainExecutor = mainExecutor;
         mSecureSettings = secureSettings;
@@ -236,6 +240,7 @@
         mUserTracker = userTracker;
         mDotViewController = dotViewController;
         mThreadFactory = threadFactory;
+        mDotFactory = dotFactory;
     }
 
     @Override
@@ -250,11 +255,14 @@
         mDotViewController.setUiExecutor(mExecutor);
     }
 
+    private boolean isPrivacyDotEnabled() {
+        return mDotFactory.getHasProviders();
+    }
+
     private void startOnScreenDecorationsThread() {
         mRotation = mContext.getDisplay().getRotation();
         mDisplayUniqueId = mContext.getDisplay().getUniqueId();
         mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
-        mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         updateRoundedCornerDrawable();
@@ -292,8 +300,9 @@
 
                     for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                         if (mOverlays[i] != null) {
-                            mOverlays[i].getViewTreeObserver().addOnPreDrawListener(
-                                    new RestartingPreDrawListener(mOverlays[i], i, newRotation));
+                            final ViewGroup overlayView = mOverlays[i].getRootView();
+                            overlayView.getViewTreeObserver().addOnPreDrawListener(
+                                    new RestartingPreDrawListener(overlayView, i, newRotation));
                         }
                     }
                 }
@@ -313,24 +322,64 @@
         updateOrientation();
     }
 
+    @Nullable
+    private View getOverlayView(@IdRes int id) {
+        if (mOverlays == null) {
+            return null;
+        }
+
+        for (final OverlayWindow overlay : mOverlays) {
+            if (overlay == null) {
+                continue;
+            }
+
+            final View view = overlay.getView(id);
+            if (view != null) {
+                return view;
+            }
+        }
+        return null;
+    }
+
+    private void removeOverlayView(@IdRes int id) {
+        if (mOverlays == null) {
+            return;
+        }
+
+        for (final OverlayWindow overlay : mOverlays) {
+            if (overlay == null) {
+                continue;
+            }
+
+            overlay.removeView(id);
+        }
+    }
+
     private void setupDecorations() {
-        if (hasRoundedCorners() || shouldDrawCutout() || mIsPrivacyDotEnabled) {
+        List<DecorProvider> decorProviders = mDotFactory.getProviders();
+
+        if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
             final DisplayCutout cutout = getCutout();
             for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                 if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
                         || shouldShowPrivacyDot(i, cutout)) {
-                    createOverlay(i, cutout);
+                    Pair<List<DecorProvider>, List<DecorProvider>> pair =
+                            DecorProviderKt.partitionAlignedBound(decorProviders, i);
+                    decorProviders = pair.getSecond();
+                    createOverlay(i, cutout, pair.getFirst());
                 } else {
                     removeOverlay(i);
                 }
             }
 
-            if (mTopLeftDot != null && mTopRightDot != null && mBottomLeftDot != null
-                    && mBottomRightDot != null) {
+            final View tl, tr, bl, br;
+            if ((tl = getOverlayView(R.id.privacy_dot_top_left_container)) != null
+                    && (tr = getOverlayView(R.id.privacy_dot_top_right_container)) != null
+                    && (bl = getOverlayView(R.id.privacy_dot_bottom_left_container)) != null
+                    && (br = getOverlayView(R.id.privacy_dot_bottom_right_container)) != null) {
                 // Overlays have been created, send the dots to the controller
                 //TODO: need a better way to do this
-                mDotViewController.initialize(
-                        mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
+                mDotViewController.initialize(tl, tr, bl, br);
             }
         } else {
             removeAllOverlays();
@@ -414,13 +463,16 @@
         if (mOverlays == null || mOverlays[pos] == null) {
             return;
         }
-        mWindowManager.removeViewImmediate(mOverlays[pos]);
+        mWindowManager.removeViewImmediate(mOverlays[pos].getRootView());
         mOverlays[pos] = null;
     }
 
-    private void createOverlay(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+    private void createOverlay(
+            @BoundsPosition int pos,
+            @Nullable DisplayCutout cutout,
+            @NonNull List<DecorProvider> decorProviders) {
         if (mOverlays == null) {
-            mOverlays = new View[BOUNDS_POSITION_LENGTH];
+            mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
         }
 
         if (mCutoutViews == null) {
@@ -430,78 +482,49 @@
         if (mOverlays[pos] != null) {
             return;
         }
-        mOverlays[pos] = overlayForPosition(pos, cutout);
+
+        mOverlays[pos] = overlayForPosition(pos, decorProviders);
 
         mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
-        ((ViewGroup) mOverlays[pos]).addView(mCutoutViews[pos]);
+        mOverlays[pos].getRootView().addView(mCutoutViews[pos]);
 
-        mOverlays[pos].setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-        mOverlays[pos].setAlpha(0);
-        mOverlays[pos].setForceDarkAllowed(false);
+        final ViewGroup overlayView = mOverlays[pos].getRootView();
+        overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        overlayView.setAlpha(0);
+        overlayView.setForceDarkAllowed(false);
 
         updateView(pos, cutout);
 
-        mWindowManager.addView(mOverlays[pos], getWindowLayoutParams(pos));
+        mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
 
-        mOverlays[pos].addOnLayoutChangeListener(new OnLayoutChangeListener() {
+        overlayView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                mOverlays[pos].removeOnLayoutChangeListener(this);
-                mOverlays[pos].animate()
+                overlayView.removeOnLayoutChangeListener(this);
+                overlayView.animate()
                         .alpha(1)
                         .setDuration(1000)
                         .start();
             }
         });
 
-        mOverlays[pos].getViewTreeObserver().addOnPreDrawListener(
-                new ValidatingPreDrawListener(mOverlays[pos]));
+        mOverlays[pos].getRootView().getViewTreeObserver().addOnPreDrawListener(
+                new ValidatingPreDrawListener(mOverlays[pos].getRootView()));
     }
 
     /**
      * Allow overrides for top/bottom positions
      */
-    private View overlayForPosition(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
-        final int layoutId = (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP)
-                ? R.layout.rounded_corners_top : R.layout.rounded_corners_bottom;
-        final ViewGroup vg = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, null);
-        initPrivacyDotView(vg, pos, cutout);
-        return vg;
-    }
-
-    private void initPrivacyDotView(@NonNull ViewGroup viewGroup, @BoundsPosition int pos,
-            @Nullable DisplayCutout cutout) {
-        final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
-        final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
-        if (!shouldShowPrivacyDot(pos, cutout)) {
-            viewGroup.removeView(left);
-            viewGroup.removeView(right);
-            return;
-        }
-
-        switch (pos) {
-            case BOUNDS_POSITION_LEFT: {
-                mTopLeftDot = left;
-                mBottomLeftDot = right;
-                break;
-            }
-            case BOUNDS_POSITION_TOP: {
-                mTopLeftDot = left;
-                mTopRightDot = right;
-                break;
-            }
-            case BOUNDS_POSITION_RIGHT: {
-                mTopRightDot = left;
-                mBottomRightDot = right;
-                break;
-            }
-            case BOUNDS_POSITION_BOTTOM: {
-                mBottomLeftDot = left;
-                mBottomRightDot = right;
-                break;
-            }
-        }
+    private OverlayWindow overlayForPosition(
+            @BoundsPosition int pos,
+            @NonNull List<DecorProvider> decorProviders) {
+        final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos);
+        decorProviders.forEach(provider -> {
+            removeOverlayView(provider.getViewId());
+            currentOverlay.addDecorProvider(provider, mRotation);
+        });
+        return currentOverlay;
     }
 
     private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
@@ -638,12 +661,15 @@
             if (mOverlays[i] == null) {
                 continue;
             }
-            final int size = ((ViewGroup) mOverlays[i]).getChildCount();
+            final ViewGroup overlayView = mOverlays[i].getRootView();
+            final int size = overlayView.getChildCount();
             View child;
             for (int j = 0; j < size; j++) {
-                child = ((ViewGroup) mOverlays[i]).getChildAt(j);
-                if (child.getId() == R.id.privacy_dot_left_container
-                        || child.getId() == R.id.privacy_dot_right_container) {
+                child = overlayView.getChildAt(j);
+                if (child.getId() == R.id.privacy_dot_top_left_container
+                        || child.getId() == R.id.privacy_dot_top_right_container
+                        || child.getId() == R.id.privacy_dot_bottom_left_container
+                        || child.getId() == R.id.privacy_dot_bottom_right_container) {
                     // Exclude privacy dot from color inversion (for now?)
                     continue;
                 }
@@ -684,13 +710,18 @@
         pw.println("ScreenDecorations state:");
         pw.println("  DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS);
         pw.println("  mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
-        pw.println("  mIsPrivacyDotEnabled:" + mIsPrivacyDotEnabled);
+        pw.println("  mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
         pw.println("  mPendingRotationChange:" + mPendingRotationChange);
         pw.println("  mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
         pw.println("  mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
                 + ")");
         pw.println("  mRoundedDefaultBottom(x,y)=(" + mRoundedDefaultBottom.x + ","
                 + mRoundedDefaultBottom.y + ")");
+        pw.println("  mOverlays(left,top,right,bottom)=("
+                + (mOverlays != null && mOverlays[BOUNDS_POSITION_LEFT] != null) + ","
+                + (mOverlays != null && mOverlays[BOUNDS_POSITION_TOP] != null) + ","
+                + (mOverlays != null && mOverlays[BOUNDS_POSITION_RIGHT] != null) + ","
+                + (mOverlays != null && mOverlays[BOUNDS_POSITION_BOTTOM] != null) + ")");
     }
 
     private void updateOrientation() {
@@ -843,7 +874,7 @@
 
     private void updateRoundedCornerView(@BoundsPosition int pos, int id,
             @Nullable DisplayCutout cutout) {
-        final View rounded = mOverlays[pos].findViewById(id);
+        final View rounded = mOverlays[pos].getRootView().findViewById(id);
         if (rounded == null) {
             return;
         }
@@ -929,7 +960,7 @@
     }
 
     private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
-        return mIsPrivacyDotEnabled && isDefaultShownOverlayPos(pos, cutout);
+        return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
     }
 
     private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
@@ -955,7 +986,7 @@
             if (mOverlays[i] == null) {
                 continue;
             }
-            mWindowManager.updateViewLayout(mOverlays[i], getWindowLayoutParams(i));
+            mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i));
         }
     }
 
@@ -1003,9 +1034,10 @@
             if (mOverlays[i] == null) {
                 continue;
             }
-            ((ImageView) mOverlays[i].findViewById(R.id.left)).setImageDrawable(
+            final ViewGroup overlayView = mOverlays[i].getRootView();
+            ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
                     isTopRoundedCorner(i, R.id.left) ? top : bottom);
-            ((ImageView) mOverlays[i].findViewById(R.id.right)).setImageDrawable(
+            ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
                     isTopRoundedCorner(i, R.id.right) ? top : bottom);
         }
     }
@@ -1047,9 +1079,10 @@
             if (mOverlays[i] == null) {
                 continue;
             }
-            setSize(mOverlays[i].findViewById(R.id.left),
+            final ViewGroup overlayView = mOverlays[i].getRootView();
+            setSize(overlayView.findViewById(R.id.left),
                     isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom);
-            setSize(mOverlays[i].findViewById(R.id.right),
+            setSize(overlayView.findViewById(R.id.right),
                     isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5ddfd75..8052c20 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -42,7 +42,6 @@
 import android.os.Trace;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -64,6 +63,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -117,7 +117,7 @@
     @NonNull private final DumpManager mDumpManager;
     @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Nullable private final Vibrator mVibrator;
+    @NonNull private final VibratorHelper mVibrator;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
@@ -506,7 +506,7 @@
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
             @NonNull ScreenLifecycle screenLifecycle,
-            @Nullable Vibrator vibrator,
+            @NonNull VibratorHelper vibrator,
             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
             @NonNull Optional<UdfpsHbmProvider> hbmProvider,
             @NonNull KeyguardStateController keyguardStateController,
@@ -577,14 +577,12 @@
      */
     @VisibleForTesting
     public void playStartHaptic() {
-        if (mVibrator != null) {
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    EFFECT_CLICK,
-                    "udfps-onStart-click",
-                    VIBRATION_ATTRIBUTES);
-        }
+        mVibrator.vibrate(
+                Process.myUid(),
+                mContext.getOpPackageName(),
+                EFFECT_CLICK,
+                "udfps-onStart-click",
+                VIBRATION_ATTRIBUTES);
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
index e231310..eaee19a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
@@ -18,16 +18,12 @@
 
 import android.media.AudioAttributes
 import android.os.VibrationEffect
-import android.os.Vibrator
-
 import com.android.keyguard.KeyguardUpdateMonitor
-
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
-
 import java.io.PrintWriter
-
 import javax.inject.Inject
 
 /**
@@ -36,7 +32,7 @@
 @SysUISingleton
 class UdfpsHapticsSimulator @Inject constructor(
     commandRegistry: CommandRegistry,
-    val vibrator: Vibrator?,
+    val vibrator: VibratorHelper,
     val keyguardUpdateMonitor: KeyguardUpdateMonitor
 ) : Command {
     val sonificationEffects =
@@ -60,13 +56,13 @@
                 }
                 "success" -> {
                     // needs to be kept up to date with AcquisitionClient#SUCCESS_VIBRATION_EFFECT
-                    vibrator?.vibrate(
+                    vibrator.vibrate(
                         VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                         sonificationEffects)
                 }
                 "error" -> {
                     // needs to be kept up to date with AcquisitionClient#ERROR_VIBRATION_EFFECT
-                    vibrator?.vibrate(
+                    vibrator.vibrate(
                         VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
                         sonificationEffects)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 41a4963..0e1cd51 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -60,8 +60,8 @@
             return;
         }
         if (mClipboardOverlayController == null) {
-            mClipboardOverlayController = new ClipboardOverlayController(mContext,
-                    new TimeoutHandler(mContext));
+            mClipboardOverlayController =
+                    new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
         }
         mClipboardOverlayController.setClipData(mClipboardManager.getPrimaryClip());
         mClipboardOverlayController.setOnSessionCompleteListener(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index caf0307..b6bcb87 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -28,6 +28,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.MainThread;
+import android.app.RemoteAction;
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ComponentName;
@@ -43,6 +44,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.input.InputManager;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Looper;
 import android.text.TextUtils;
 import android.util.Log;
@@ -60,8 +62,13 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.policy.PhoneWindow;
@@ -71,6 +78,7 @@
 import com.android.systemui.screenshot.TimeoutHandler;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 /**
  * Controls state and UI for the overlay that appears when something is added to the clipboard
@@ -93,6 +101,7 @@
     private final PhoneWindow mWindow;
     private final TimeoutHandler mTimeoutHandler;
     private final AccessibilityManager mAccessibilityManager;
+    private final TextClassifier mTextClassifier;
 
     private final DraggableConstraintLayout mView;
     private final ImageView mImagePreview;
@@ -101,9 +110,13 @@
     private final ScreenshotActionChip mRemoteCopyChip;
     private final View mActionContainerBackground;
     private final View mDismissButton;
+    private final LinearLayout mActionContainer;
+    private final ArrayList<ScreenshotActionChip> mActionChips = new ArrayList<>();
 
     private Runnable mOnSessionCompleteListener;
 
+
+    private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
 
     private BroadcastReceiver mCloseDialogsReceiver;
@@ -117,6 +130,8 @@
         mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
 
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+        mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
+                .getTextClassifier();
 
         mWindowManager = mContext.getSystemService(WindowManager.class);
 
@@ -134,8 +149,9 @@
 
         mView = (DraggableConstraintLayout)
                 LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null);
-        mActionContainerBackground = requireNonNull(
-                mView.findViewById(R.id.actions_container_background));
+        mActionContainerBackground =
+                requireNonNull(mView.findViewById(R.id.actions_container_background));
+        mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
         mImagePreview = requireNonNull(mView.findViewById(R.id.image_preview));
         mTextPreview = requireNonNull(mView.findViewById(R.id.text_preview));
         mEditChip = requireNonNull(mView.findViewById(R.id.edit_chip));
@@ -143,7 +159,7 @@
         mDismissButton = requireNonNull(mView.findViewById(R.id.dismiss_button));
 
         mView.setOnDismissCallback(this::hideImmediate);
-        mView.setOnInteractionCallback(() -> mTimeoutHandler.resetTimeout());
+        mView.setOnInteractionCallback(mTimeoutHandler::resetTimeout);
 
         mDismissButton.setOnClickListener(view -> animateOut());
 
@@ -166,7 +182,7 @@
         withWindowAttached(() -> {
             mWindow.setContentView(mView);
             updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
-            mView.post(() -> getEnterAnimation().start());
+            mView.post(this::animateIn);
         });
 
         mTimeoutHandler.setOnTimeoutRunnable(this::animateOut);
@@ -199,12 +215,15 @@
 
     void setClipData(ClipData clipData) {
         reset();
-
         if (clipData == null || clipData.getItemCount() == 0) {
-            showTextPreview(
-                    mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
+            showTextPreview(mContext.getResources().getString(
+                    R.string.clipboard_overlay_text_copied));
         } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
-            showEditableText(clipData.getItemAt(0).getText());
+            ClipData.Item item = clipData.getItemAt(0);
+            if (item.getTextLinks() != null) {
+                AsyncTask.execute(() -> classifyText(clipData.getItemAt(0)));
+            }
+            showEditableText(item.getText());
         } else if (clipData.getItemAt(0).getUri() != null) {
             // How to handle non-image URIs?
             showEditableImage(clipData.getItemAt(0).getUri());
@@ -212,7 +231,6 @@
             showTextPreview(
                     mContext.getResources().getString(R.string.clipboard_overlay_text_copied));
         }
-
         mTimeoutHandler.resetTimeout();
     }
 
@@ -220,10 +238,40 @@
         mOnSessionCompleteListener = runnable;
     }
 
+    private void classifyText(ClipData.Item item) {
+        ArrayList<RemoteAction> actions = new ArrayList<>();
+        for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
+            TextClassification classification = mTextClassifier.classifyText(
+                    item.getText(), link.getStart(), link.getEnd(), null);
+            actions.addAll(classification.getActions());
+        }
+        mView.post(() -> {
+            for (ScreenshotActionChip chip : mActionChips) {
+                mActionContainer.removeView(chip);
+            }
+            mActionChips.clear();
+            for (RemoteAction action : actions) {
+                ScreenshotActionChip chip = constructActionChip(action);
+                mActionContainer.addView(chip);
+                mActionChips.add(chip);
+            }
+        });
+    }
+
+    private ScreenshotActionChip constructActionChip(RemoteAction action) {
+        ScreenshotActionChip chip = (ScreenshotActionChip) LayoutInflater.from(mContext).inflate(
+                R.layout.screenshot_action_chip, mActionContainer, false);
+        chip.setText(action.getTitle());
+        chip.setIcon(action.getIcon(), false);
+        chip.setPendingIntent(action.getActionIntent(), this::animateOut);
+        chip.setAlpha(1);
+        return chip;
+    }
+
     private void monitorOutsideTouches() {
         InputManager inputManager = mContext.getSystemService(InputManager.class);
-        InputMonitor monitor = inputManager.monitorGestureInput("clipboard overlay", 0);
-        mInputEventReceiver = new InputEventReceiver(monitor.getInputChannel(),
+        mInputMonitor = inputManager.monitorGestureInput("clipboard overlay", 0);
+        mInputEventReceiver = new InputEventReceiver(mInputMonitor.getInputChannel(),
                 Looper.getMainLooper()) {
             @Override
             public void onInputEvent(InputEvent event) {
@@ -311,6 +359,10 @@
         return nearbyIntent;
     }
 
+    private void animateIn() {
+        getEnterAnimation().start();
+    }
+
     private void animateOut() {
         getExitAnimation().start();
     }
@@ -390,6 +442,10 @@
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
         }
+        if (mInputMonitor != null) {
+            mInputMonitor.dispose();
+            mInputMonitor = null;
+        }
         if (mOnSessionCompleteListener != null) {
             mOnSessionCompleteListener.run();
         }
@@ -398,6 +454,10 @@
     private void reset() {
         mView.setTranslationX(0);
         mView.setAlpha(0);
+        for (ScreenshotActionChip chip : mActionChips) {
+            mActionContainer.removeView(chip);
+        }
+        mActionChips.clear();
         mTimeoutHandler.cancelTimeout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index a29f3e9..f87fa96 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.os.VibrationEffect
-import android.os.Vibrator
 import android.service.controls.Control
 import android.service.controls.actions.BooleanAction
 import android.service.controls.actions.CommandAction
@@ -32,16 +31,14 @@
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.wm.shell.TaskViewFactory
-import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
 
@@ -52,14 +49,11 @@
     @Main private val uiExecutor: DelayableExecutor,
     private val activityStarter: ActivityStarter,
     private val keyguardStateController: KeyguardStateController,
-    private val globalActionsComponent: GlobalActionsComponent,
     private val taskViewFactory: Optional<TaskViewFactory>,
-    private val broadcastDispatcher: BroadcastDispatcher,
-    private val lazyUiController: Lazy<ControlsUiController>,
-    private val controlsMetricsLogger: ControlsMetricsLogger
+    private val controlsMetricsLogger: ControlsMetricsLogger,
+    private val vibrator: VibratorHelper
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
-    private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
     private var pendingAction: Action? = null
     private var actionsInProgress = mutableSetOf<String>()
     private val isLocked: Boolean
@@ -194,7 +188,7 @@
     }
 
     private fun vibrate(effect: VibrationEffect) {
-        bgExecutor.execute { vibrator.vibrate(effect) }
+        vibrator.vibrate(effect)
     }
 
     private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) {
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
new file mode 100644
index 0000000..3543bb4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
@@ -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.systemui.decor
+import android.view.DisplayCutout
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+
+/**
+ * An interface for providing view with a specific functionality. Take an example, if privacy dot
+ * is enabled, there are 4 DecorProviders which are used to provide privacy dot views on top-left,
+ * top-right, bottom-left, bottom-right.
+ */
+abstract class DecorProvider {
+
+    /** Id for the view which is created through inflateView() */
+    abstract val viewId: Int
+
+    /** The number of total aligned bounds */
+    val numOfAlignedEdge: Int
+    get() = alignedBounds.size
+
+    /** The aligned bounds for the view which is created through inflateView() */
+    abstract val alignedBounds: List<Int>
+
+    /** Inflate view into parent as current rotation */
+    abstract fun inflateView(
+        inflater: LayoutInflater,
+        parent: ViewGroup,
+        @Surface.Rotation rotation: Int
+    ): View
+}
+
+/**
+ * Split list to 2 list, and return it back as Pair<>. The providers on the first list contains this
+ * alignedBound element. The providers on the second list do not contain this alignedBound element
+ */
+fun List<DecorProvider>.partitionAlignedBound(
+    @DisplayCutout.BoundsPosition alignedBound: Int
+): Pair<List<DecorProvider>, List<DecorProvider>> {
+    return partition { it.alignedBounds.contains(alignedBound) }
+}
+
+/**
+ * A provider for view shown on corner.
+ */
+abstract class CornerDecorProvider : DecorProvider() {
+    /** The first bound which a corner view is aligned based on rotation 0 */
+    @DisplayCutout.BoundsPosition protected abstract val alignedBound1: Int
+    /** The second bound which a corner view is aligned based on rotation 0 */
+    @DisplayCutout.BoundsPosition protected abstract val alignedBound2: Int
+
+    override val alignedBounds: List<Int> by lazy {
+        listOf(alignedBound1, alignedBound2)
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
similarity index 71%
copy from core/java/android/view/selectiontoolbar/SelectionContext.aidl
copy to packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
index 5206831..c60cad8 100644
--- a/core/java/android/view/selectiontoolbar/SelectionContext.aidl
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.view.selectiontoolbar;
+package com.android.systemui.decor
 
-/**
- * @hide
- */
-parcelable SelectionContext;
+abstract class DecorProviderFactory {
+    abstract val providers: List<DecorProvider>
+    abstract val hasProviders: Boolean
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
new file mode 100644
index 0000000..9f8679c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.decor
+
+import android.annotation.IdRes
+import android.view.DisplayCutout
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import java.util.HashMap
+
+class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) {
+
+    private val layoutId: Int
+    get() {
+        return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT ||
+                pos == DisplayCutout.BOUNDS_POSITION_TOP) {
+            R.layout.rounded_corners_top
+        } else {
+            R.layout.rounded_corners_bottom
+        }
+    }
+
+    val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup
+    private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap()
+
+    fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) {
+        val view = decorProvider.inflateView(layoutInflater, rootView, rotation)
+        viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider)
+    }
+
+    fun getView(@IdRes id: Int): View? {
+        val pair = viewProviderMap[id]
+        return pair?.first
+    }
+
+    fun removeView(@IdRes id: Int) {
+        val view = getView(id)
+        if (view != null) {
+            rootView.removeView(view)
+            viewProviderMap.remove(id)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
new file mode 100644
index 0000000..7afd7e0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.decor
+
+import android.content.res.Resources
+import android.view.DisplayCutout
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Surface
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+@SysUISingleton
+class PrivacyDotDecorProviderFactory @Inject constructor(
+    @Main private val res: Resources
+) : DecorProviderFactory() {
+
+    private val isPrivacyDotEnabled: Boolean
+        get() = res.getBoolean(R.bool.config_enablePrivacyDot)
+
+    override val hasProviders: Boolean
+        get() = isPrivacyDotEnabled
+
+    override val providers: List<DecorProvider>
+        get() {
+            return if (hasProviders) {
+                listOf(
+                    PrivacyDotCornerDecorProviderImpl(
+                        viewId = R.id.privacy_dot_top_left_container,
+                        alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                        alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                        layoutId = R.layout.privacy_dot_top_left),
+                    PrivacyDotCornerDecorProviderImpl(
+                        viewId = R.id.privacy_dot_top_right_container,
+                        alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                        alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                        layoutId = R.layout.privacy_dot_top_right),
+                    PrivacyDotCornerDecorProviderImpl(
+                        viewId = R.id.privacy_dot_bottom_left_container,
+                        alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                        alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                        layoutId = R.layout.privacy_dot_bottom_left),
+                    PrivacyDotCornerDecorProviderImpl(
+                        viewId = R.id.privacy_dot_bottom_right_container,
+                        alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                        alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                        layoutId = R.layout.privacy_dot_bottom_right)
+                )
+            } else {
+                emptyList()
+            }
+        }
+}
+
+class PrivacyDotCornerDecorProviderImpl(
+    override val viewId: Int,
+    @DisplayCutout.BoundsPosition override val alignedBound1: Int,
+    @DisplayCutout.BoundsPosition override val alignedBound2: Int,
+    private val layoutId: Int
+) : CornerDecorProvider() {
+
+    override fun inflateView(
+        inflater: LayoutInflater,
+        parent: ViewGroup,
+        @Surface.Rotation rotation: Int
+    ): View {
+        inflater.inflate(layoutId, parent, true)
+        return parent.getChildAt(parent.childCount - 1 /* latest new added child */)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 16ed1fb..3ee0cad 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,9 +29,12 @@
 import androidx.lifecycle.ViewModelStore;
 
 import com.android.internal.policy.PhoneWindow;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.complication.Complication;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
 
 import java.util.concurrent.Executor;
 
@@ -53,6 +56,7 @@
     // A controller for the dream overlay container view (which contains both the status bar and the
     // content area).
     private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     // A reference to the {@link Window} used to hold the dream overlay.
     private Window mWindow;
@@ -68,19 +72,40 @@
 
     private ViewModelStore mViewModelStore = new ViewModelStore();
 
+    private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+
+    private final KeyguardUpdateMonitorCallback mKeyguardCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onShadeExpandedChanged(boolean expanded) {
+                    if (mLifecycleRegistry.getCurrentState() != Lifecycle.State.RESUMED
+                            && mLifecycleRegistry.getCurrentState() != Lifecycle.State.STARTED) {
+                        return;
+                    }
+
+                    mLifecycleRegistry.setCurrentState(
+                            expanded ? Lifecycle.State.STARTED : Lifecycle.State.RESUMED);
+                }
+            };
+
     @Inject
     public DreamOverlayService(
             Context context,
             @Main Executor executor,
-            DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+            DreamOverlayComponent.Factory dreamOverlayComponentFactory,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mExecutor = executor;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
 
         final DreamOverlayComponent component =
                 dreamOverlayComponentFactory.create(mViewModelStore, mHost);
         mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController();
         setCurrentState(Lifecycle.State.CREATED);
         mLifecycleRegistry = component.getLifecycleRegistry();
+        mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor();
+        mDreamOverlayTouchMonitor.init();
     }
 
     private void setCurrentState(Lifecycle.State state) {
@@ -89,6 +114,7 @@
 
     @Override
     public void onDestroy() {
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
         setCurrentState(Lifecycle.State.DESTROYED);
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         windowManager.removeView(mWindow.getDecorView());
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 d5053a0..3d2f924 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.dreams.dagger;
 
+import com.android.systemui.dreams.touch.dagger.DreamTouchModule;
+
 import dagger.Module;
 
 /**
  * Dagger Module providing Communal-related functionality.
  */
-@Module(subcomponents = {
-        DreamOverlayComponent.class,
-})
+@Module(includes = {
+            DreamTouchModule.class,
+        },
+        subcomponents = {
+            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 f0ab696..05ab901 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -25,6 +25,7 @@
 import com.android.systemui.dreams.DreamOverlayContainerViewController;
 import com.android.systemui.dreams.complication.Complication;
 import com.android.systemui.dreams.complication.dagger.ComplicationModule;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -64,4 +65,7 @@
 
     /** Builds a {@link androidx.lifecycle.LifecycleOwner} */
     LifecycleOwner getLifecycleOwner();
+
+    /** Builds a {@link DreamOverlayTouchMonitor} */
+    DreamOverlayTouchMonitor getDreamOverlayTouchMonitor();
 }
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 b56aa2c..503817a 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,7 @@
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
+import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 
@@ -33,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayContainerView;
 import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
@@ -42,6 +44,7 @@
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.IntoSet;
 
 /** Dagger module for {@link DreamOverlayComponent}. */
 @Module
@@ -140,4 +143,18 @@
     static LifecycleRegistry providesLifecycleRegistry(LifecycleOwner lifecycleOwner) {
         return new LifecycleRegistry(lifecycleOwner);
     }
+
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
+        return lifecycleOwner.getLifecycle();
+    }
+
+    // TODO: This stub should be removed once there is a {@link DreamTouchHandler}
+    // implementation present.
+    @Provides
+    @IntoSet
+    static DreamTouchHandler provideDreamTouchHandler() {
+        return session -> { };
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
new file mode 100644
index 0000000..3e5efb2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -0,0 +1,368 @@
+/*
+ * 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.touch;
+
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamOverlayTouchMonitor} is responsible for monitoring touches and gestures over the
+ * dream overlay and redirecting them to a set of listeners. This monitor is in charge of figuring
+ * out when listeners are eligible for receiving touches and filtering the listener pool if
+ * touches are consumed.
+ */
+public class DreamOverlayTouchMonitor {
+    // This executor is used to protect {@code mActiveTouchSessions} from being modified
+    // concurrently. Any operation that adds or removes values should use this executor.
+    private final Executor mExecutor;
+    private final Lifecycle mLifecycle;
+
+    /**
+     * Adds a new {@link TouchSessionImpl} to participate in receiving future touches and gestures.
+     */
+    private ListenableFuture<DreamTouchHandler.TouchSession> push(
+            TouchSessionImpl touchSessionImpl) {
+        return CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                if (!mActiveTouchSessions.remove(touchSessionImpl)) {
+                    completer.set(null);
+                    return;
+                }
+
+                final TouchSessionImpl touchSession =
+                        new TouchSessionImpl(this, touchSessionImpl);
+                mActiveTouchSessions.add(touchSession);
+                completer.set(touchSession);
+            });
+
+            return "DreamOverlayTouchMonitor::push";
+        });
+    }
+
+    /**
+     * Removes a {@link TouchSessionImpl} from receiving further updates.
+     */
+    private ListenableFuture<DreamTouchHandler.TouchSession> pop(
+            TouchSessionImpl touchSessionImpl) {
+        return CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                if (mActiveTouchSessions.remove(touchSessionImpl)) {
+                    touchSessionImpl.onRemoved();
+
+                    final TouchSessionImpl predecessor = touchSessionImpl.getPredecessor();
+
+                    if (predecessor != null) {
+                        mActiveTouchSessions.add(predecessor);
+                    }
+
+                    completer.set(predecessor);
+                }
+            });
+
+            return "DreamOverlayTouchMonitor::pop";
+        });
+    }
+
+    /**
+     * {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
+     * {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
+     * and provides the associated client with access to the monitor.
+     */
+    private static class TouchSessionImpl implements DreamTouchHandler.TouchSession {
+        private final HashSet<InputChannelCompat.InputEventListener> mEventListeners =
+                new HashSet<>();
+        private final HashSet<GestureDetector.OnGestureListener> mGestureListeners =
+                new HashSet<>();
+        private final HashSet<Callback> mCallbacks = new HashSet<>();
+
+        private final TouchSessionImpl mPredecessor;
+        private final DreamOverlayTouchMonitor mTouchMonitor;
+
+        TouchSessionImpl(DreamOverlayTouchMonitor touchMonitor, TouchSessionImpl predecessor) {
+            mPredecessor = predecessor;
+            mTouchMonitor = touchMonitor;
+        }
+
+        @Override
+        public void registerCallback(Callback callback) {
+            mCallbacks.add(callback);
+        }
+
+        @Override
+        public boolean registerInputListener(
+                InputChannelCompat.InputEventListener inputEventListener) {
+            return mEventListeners.add(inputEventListener);
+        }
+
+        @Override
+        public boolean registerGestureListener(GestureDetector.OnGestureListener gestureListener) {
+            return mGestureListeners.add(gestureListener);
+        }
+
+        @Override
+        public ListenableFuture<DreamTouchHandler.TouchSession> push() {
+            return mTouchMonitor.push(this);
+        }
+
+        @Override
+        public ListenableFuture<DreamTouchHandler.TouchSession> pop() {
+            return mTouchMonitor.pop(this);
+        }
+
+        /**
+         * Returns the active listeners to receive touch events.
+         */
+        public Collection<InputChannelCompat.InputEventListener> getEventListeners() {
+            return mEventListeners;
+        }
+
+        /**
+         * Returns the active listeners to receive gesture events.
+         */
+        public Collection<GestureDetector.OnGestureListener> getGestureListeners() {
+            return mGestureListeners;
+        }
+
+        /**
+         * Returns the {@link TouchSessionImpl} that preceded this current session. This will
+         * become the new active session when this session is popped.
+         */
+        private TouchSessionImpl getPredecessor() {
+            return mPredecessor;
+        }
+
+        /**
+         * Called by the monitor when this session is removed.
+         */
+        private void onRemoved() {
+            mCallbacks.forEach(callback -> callback.onRemoved());
+        }
+    }
+
+    /**
+     * This lifecycle observer ensures touch monitoring only occurs while the overlay is "resumed".
+     * This concept is mapped over from the equivalent view definition: The {@link LifecycleOwner}
+     * will report the dream is not resumed when it is obscured (from the notification shade being
+     * expanded for example) or not active (such as when it is destroyed).
+     */
+    private final LifecycleObserver mLifecycleObserver = new DefaultLifecycleObserver() {
+        @Override
+        public void onResume(@NonNull LifecycleOwner owner) {
+            startMonitoring();
+        }
+
+        @Override
+        public void onPause(@NonNull LifecycleOwner owner) {
+            stopMonitoring();
+        }
+    };
+
+    /**
+     * When invoked, instantiates a new {@link InputSession} to monitor touch events.
+     */
+    private void startMonitoring() {
+        stopMonitoring();
+        mCurrentInputSession = mInputSessionFactory.create(
+                "dreamOverlay",
+                mInputEventListener,
+                mOnGestureListener,
+                true)
+                .getInputSession();
+    }
+
+    /**
+     * Destroys any active {@link InputSession}.
+     */
+    private void stopMonitoring() {
+        if (mCurrentInputSession == null) {
+            return;
+        }
+
+        mCurrentInputSession.dispose();
+        mCurrentInputSession = null;
+    }
+
+
+    private final HashSet<TouchSessionImpl> mActiveTouchSessions = new HashSet<>();
+    private final Collection<DreamTouchHandler> mHandlers;
+
+    private InputChannelCompat.InputEventListener mInputEventListener =
+            new InputChannelCompat.InputEventListener() {
+        @Override
+        public void onInputEvent(InputEvent ev) {
+            // No Active sessions are receiving touches. Create sessions for each listener
+            if (mActiveTouchSessions.isEmpty()) {
+                for (DreamTouchHandler handler : mHandlers) {
+                    final TouchSessionImpl sessionStack =
+                            new TouchSessionImpl(DreamOverlayTouchMonitor.this, null);
+                    mActiveTouchSessions.add(sessionStack);
+                    handler.onSessionStart(sessionStack);
+                }
+            }
+
+            // Find active sessions and invoke on InputEvent.
+            mActiveTouchSessions.stream()
+                    .map(touchSessionStack -> touchSessionStack.getEventListeners())
+                    .flatMap(Collection::stream)
+                    .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
+        }
+    };
+
+    /**
+     * The {@link Evaluator} interface allows for callers to inspect a listener from the
+     * {@link android.view.GestureDetector.OnGestureListener} set. This helps reduce duplicated
+     * iteration loops over this set.
+     */
+    private interface Evaluator {
+        boolean evaluate(GestureDetector.OnGestureListener listener);
+    }
+
+    private GestureDetector.OnGestureListener mOnGestureListener =
+            new GestureDetector.OnGestureListener() {
+        private boolean evaluate(Evaluator evaluator) {
+            final Set<TouchSessionImpl> consumingSessions = new HashSet<>();
+
+            // When a gesture is consumed, it is assumed that all touches for the current session
+            // should be directed only to those TouchSessions until those sessions are popped. All
+            // non-participating sessions are removed from receiving further updates with
+            // {@link DreamOverlayTouchMonitor#isolate}.
+            final boolean eventConsumed = mActiveTouchSessions.stream()
+                    .map(touchSession -> {
+                        boolean consume = touchSession.getGestureListeners()
+                                .stream()
+                                .map(listener -> evaluator.evaluate(listener))
+                                .anyMatch(consumed -> consumed);
+
+                        if (consume) {
+                            consumingSessions.add(touchSession);
+                        }
+                        return consume;
+                    }).anyMatch(consumed -> consumed);
+
+            if (eventConsumed) {
+                DreamOverlayTouchMonitor.this.isolate(consumingSessions);
+            }
+
+            return eventConsumed;
+        }
+
+        // This method is called for gesture events that cannot be consumed.
+        private void observe(Consumer<GestureDetector.OnGestureListener> consumer) {
+            mActiveTouchSessions.stream()
+                    .map(touchSession -> touchSession.getGestureListeners())
+                    .flatMap(Collection::stream)
+                    .forEach(listener -> consumer.accept(listener));
+        }
+
+        @Override
+        public boolean onDown(MotionEvent e) {
+            return evaluate(listener -> listener.onDown(e));
+        }
+
+        @Override
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+            return evaluate(listener -> listener.onFling(e1, e2, velocityX, velocityY));
+        }
+
+        @Override
+        public void onLongPress(MotionEvent e) {
+            observe(listener -> listener.onLongPress(e));
+        }
+
+        @Override
+        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+            return evaluate(listener -> listener.onScroll(e1, e2, distanceX, distanceY));
+        }
+
+        @Override
+        public void onShowPress(MotionEvent e) {
+            observe(listener -> listener.onShowPress(e));
+        }
+
+        @Override
+        public boolean onSingleTapUp(MotionEvent e) {
+            return evaluate(listener -> listener.onSingleTapUp(e));
+        }
+    };
+
+    private InputSessionComponent.Factory mInputSessionFactory;
+    private InputSession mCurrentInputSession;
+
+    /**
+     * Designated constructor for {@link DreamOverlayTouchMonitor}
+     * @param executor This executor will be used for maintaining the active listener list to avoid
+     *                 concurrent modification.
+     * @param lifecycle {@link DreamOverlayTouchMonitor} will listen to this lifecycle to determine
+     *                                                  whether touch monitoring should be active.
+     * @param inputSessionFactory This factory will generate the {@link InputSession} requested by
+     *                            the monitor. Each session should be unique and valid when
+     *                            returned.
+     * @param handlers This set represents the {@link DreamTouchHandler} instances that will
+     *                 participate in touch handling.
+     */
+    @Inject
+    public DreamOverlayTouchMonitor(
+            @Main Executor executor,
+            Lifecycle lifecycle,
+            InputSessionComponent.Factory inputSessionFactory,
+            Set<DreamTouchHandler> handlers) {
+        mHandlers = handlers;
+        mInputSessionFactory = inputSessionFactory;
+        mExecutor = executor;
+        mLifecycle = lifecycle;
+    }
+
+    /**
+     * Initializes the monitor. should only be called once after creation.
+     */
+    public void init() {
+        mLifecycle.addObserver(mLifecycleObserver);
+    }
+
+    private void isolate(Set<TouchSessionImpl> sessions) {
+        Collection<TouchSessionImpl> removedSessions = mActiveTouchSessions.stream()
+                .filter(touchSession -> !sessions.contains(touchSession))
+                .collect(Collectors.toCollection(HashSet::new));
+
+        removedSessions.forEach(touchSession -> touchSession.onRemoved());
+
+        mActiveTouchSessions.removeAll(removedSessions);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
new file mode 100644
index 0000000..c73ff73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.touch;
+
+import android.view.GestureDetector;
+
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The {@link DreamTouchHandler} interface provides a way for dream overlay components to observe
+ * touch events and gestures with the ability to intercept the latter. Touch interaction sequences
+ * are abstracted as sessions. A session represents the time of first
+ * {@code android.view.MotionEvent.ACTION_DOWN} event to the last {@link DreamTouchHandler}
+ * stopping interception of gestures. If no gesture is intercepted, the session continues
+ * indefinitely. {@link DreamTouchHandler} have the ability to create a stack of sessions, which
+ * allows for motion logic to be captured in modal states.
+ */
+public interface DreamTouchHandler {
+    /**
+     * A touch session captures the interaction surface of a {@link DreamTouchHandler}. Clients
+     * register listeners as desired to participate in motion/gesture callbacks.
+     */
+    interface TouchSession {
+        interface Callback {
+            void onRemoved();
+        }
+
+        void registerCallback(Callback callback);
+
+        /**
+         * Adds a input event listener for the given session.
+         * @param inputEventListener
+         */
+        boolean registerInputListener(InputChannelCompat.InputEventListener inputEventListener);
+
+        /**
+         * Adds a gesture listener for the given session.
+         * @param gestureListener
+         */
+        boolean registerGestureListener(GestureDetector.OnGestureListener gestureListener);
+
+        /**
+         * Creates a new {@link TouchSession} that will receive any updates that would have been
+         * directed to this {@link TouchSession}.
+         * @return The future which will return a new {@link TouchSession} that will receive
+         * subsequent events. If the operation fails, {@code null} will be returned.
+         */
+        ListenableFuture<TouchSession> push();
+
+        /**
+         * Explicitly releases this {@link TouchSession}. The registered listeners will no longer
+         * receive any further updates.
+         * @return The future containing the {@link TouchSession} that will receive subsequent
+         * events. This session will be the direct predecessor of the popped session. {@code null}
+         * if the popped {@link TouchSession} was the initial session or has already been popped.
+         */
+        ListenableFuture<TouchSession> pop();
+    }
+
+    /**
+     * Informed a new touch session has begun. The first touch event will be delivered to any
+     * listener registered through
+     * {@link TouchSession#registerInputListener(InputChannelCompat.InputEventListener)} during this
+     * call. If there are no interactions with this touch session after this method returns, it will
+     * be dropped.
+     * @param session
+     */
+    void onSessionStart(TouchSession session);
+
+    /**
+     * Invoked when a session has ended. This will be invoked for every session completion, even
+     * those that are removed through {@link TouchSession#pop()}.
+     * @param session
+     */
+    default void onSessionEnd(TouchSession session) { }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
new file mode 100644
index 0000000..4382757
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/InputSession.java
@@ -0,0 +1,90 @@
+/*
+ * 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.touch;
+
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link InputSession} encapsulates components behind input monitoring and handles their lifecycle.
+ * Sessions are meant to be disposable; actions such as exclusively capturing touch events is modal
+ * and destroying the sessions allows a reset. Additionally, {@link InputSession} is meant to have
+ * a single listener for input and gesture. Any broadcasting must be accomplished elsewhere.
+ */
+public class InputSession {
+    private final InputMonitorCompat mInputMonitor;
+    private final InputChannelCompat.InputEventReceiver mInputEventReceiver;
+    private final GestureDetector mGestureDetector;
+
+    /**
+     * Default session constructor.
+     * @param sessionName The session name that will be applied to the underlying
+     * {@link InputMonitorCompat}.
+     * @param inputEventListener A listener to receive input events.
+     * @param gestureListener A listener to receive gesture events.
+     * @param pilferOnGestureConsume Whether touch events should be pilfered after a gesture has
+     *                               been consumed.
+     */
+    @Inject
+    public InputSession(@Named(INPUT_SESSION_NAME) String sessionName,
+            InputChannelCompat.InputEventListener inputEventListener,
+            GestureDetector.OnGestureListener gestureListener,
+            @Named(PILFER_ON_GESTURE_CONSUME) boolean pilferOnGestureConsume) {
+        mInputMonitor = new InputMonitorCompat(sessionName, Display.DEFAULT_DISPLAY);
+        mGestureDetector = new GestureDetector(gestureListener);
+
+        mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
+                Choreographer.getInstance(),
+                ev -> {
+                    // Process event. Since sometimes input may be a prerequisite for some
+                    // gesture logic, process input first.
+                    inputEventListener.onInputEvent(ev);
+
+                    if (ev instanceof MotionEvent
+                            && mGestureDetector.onTouchEvent((MotionEvent) ev)
+                            && pilferOnGestureConsume) {
+                        mInputMonitor.pilferPointers();
+                    }
+                });
+    }
+
+    /**
+     * Destroys the {@link InputSession}, removing any component from listening to future touch
+     * events.
+     */
+    public void dispose() {
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dispose();
+        }
+
+        if (mInputMonitor != null) {
+            mInputMonitor.dispose();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
new file mode 100644
index 0000000..7b77b59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -0,0 +1,30 @@
+/*
+ * 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.touch.dagger;
+
+import dagger.Module;
+
+/**
+ * {@link DreamTouchModule} encapsulates dream touch-related components.
+ */
+@Module(subcomponents = {
+        InputSessionComponent.class,
+})
+public interface DreamTouchModule {
+    String INPUT_SESSION_NAME = "INPUT_SESSION_NAME";
+    String PILFER_ON_GESTURE_CONSUME = "PILFER_ON_GESTURE_CONSUME";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
new file mode 100644
index 0000000..ad59a2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/InputSessionComponent.java
@@ -0,0 +1,51 @@
+/*
+ * 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.touch.dagger;
+
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.INPUT_SESSION_NAME;
+import static com.android.systemui.dreams.touch.dagger.DreamTouchModule.PILFER_ON_GESTURE_CONSUME;
+
+import android.view.GestureDetector;
+
+import com.android.systemui.dreams.touch.InputSession;
+import com.android.systemui.shared.system.InputChannelCompat;
+
+import javax.inject.Named;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * {@link InputSessionComponent} generates {@link InputSession} with specific instances bound for
+ * the session name and whether touches should be pilfered when consumed.
+ */
+@Subcomponent
+public interface InputSessionComponent {
+    /**
+     * Generates {@link InputSessionComponent}.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        InputSessionComponent create(@Named(INPUT_SESSION_NAME) @BindsInstance String name,
+                @BindsInstance InputChannelCompat.InputEventListener inputEventListener,
+                @BindsInstance GestureDetector.OnGestureListener gestureListener,
+                @Named(PILFER_ON_GESTURE_CONSUME) @BindsInstance boolean pilferOnGestureConsume);
+    }
+
+    /** */
+    InputSession getInputSession();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 490f7c1..97edf3b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -41,7 +41,7 @@
     /***************************************/
     // 100 - notification
     public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
-            new BooleanFlag(101, false);
+            new BooleanFlag(101, true);
 
     public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
             new BooleanFlag(103, false);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 2ebcd853..f0371fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -62,7 +62,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.sysprop.TelephonyProperties;
@@ -119,6 +118,7 @@
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.scrim.ScrimDrawable;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -327,7 +327,7 @@
             TelephonyListenerManager telephonyListenerManager,
             GlobalSettings globalSettings,
             SecureSettings secureSettings,
-            @Nullable Vibrator vibrator,
+            @NonNull VibratorHelper vibrator,
             @Main Resources resources,
             ConfigurationController configurationController,
             KeyguardStateController keyguardStateController,
@@ -397,7 +397,7 @@
         mGlobalSettings.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
                 mAirplaneModeObserver);
-        mHasVibrator = vibrator != null && vibrator.hasVibrator();
+        mHasVibrator = vibrator.hasVibrator();
 
         mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean(
                 R.bool.config_useFixedVolume);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 441e79a..ec15b24 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -304,6 +305,7 @@
                         allowSystemGestureIgnoringBarVisibility())
                 .setFlag(SYSUI_STATE_SCREEN_PINNING,
                         ActivityManagerWrapper.getInstance().isScreenPinningActive())
+                .setFlag(SYSUI_STATE_IMMERSIVE_MODE, isImmersiveMode())
                 .commitUpdate(mDisplayId);
     }
 
@@ -445,6 +447,10 @@
         return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
     }
 
+    private boolean isImmersiveMode() {
+        return mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration configuration) {
         mEdgeBackGestureHandler.onConfigurationChanged(configuration);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
index 7e5b554..7fb58f0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.SystemClock;
+import android.util.FloatProperty;
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -44,6 +45,20 @@
     public static final int VERTICAL = 1;  // Consume taps along the left edge.
 
     private static final boolean CHATTY = true; // print to logcat when we eat a click
+
+    private static final FloatProperty<DeadZone> FLASH_PROPERTY =
+            new FloatProperty<DeadZone>("DeadZoneFlash") {
+        @Override
+        public void setValue(DeadZone object, float value) {
+            object.setFlash(value);
+        }
+
+        @Override
+        public Float get(DeadZone object) {
+            return object.getFlash();
+        }
+    };
+
     private final NavigationBarController mNavBarController;
     private final NavigationBarView mNavigationBarView;
 
@@ -63,7 +78,7 @@
     private final Runnable mDebugFlash = new Runnable() {
         @Override
         public void run() {
-            ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+            ObjectAnimator.ofFloat(DeadZone.this, FLASH_PROPERTY, 1f, 0f).setDuration(150).start();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index 6c01f0e..dec5afd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -21,7 +21,6 @@
 import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -129,7 +128,6 @@
             iconParams.setMarginStart(paddingHorizontal);
             iconParams.setMarginEnd(paddingHorizontal);
         }
-        mTextView.setVisibility(hasText ? View.VISIBLE : View.GONE);
         mIconView.setLayoutParams(iconParams);
         mTextView.setLayoutParams(textParams);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 6c3a909..c74621d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -16,13 +16,19 @@
 
 package com.android.systemui.statusbar;
 
-import android.content.Context;
-import android.os.AsyncTask;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -32,21 +38,75 @@
 public class VibratorHelper {
 
     private final Vibrator mVibrator;
-    private final Context mContext;
     private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+    private final Executor mExecutor;
 
     /**
      */
     @Inject
-    public VibratorHelper(Context context) {
-        mContext = context;
-        mVibrator = context.getSystemService(Vibrator.class);
+    public VibratorHelper(@Nullable Vibrator vibrator, @Background Executor executor) {
+        mExecutor = executor;
+        mVibrator = vibrator;
     }
 
+    /**
+     * @see Vibrator#vibrate(long)
+     */
     public void vibrate(final int effectId) {
-        AsyncTask.execute(() ->
+        if (!hasVibrator()) {
+            return;
+        }
+        mExecutor.execute(() ->
                 mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
                         TOUCH_VIBRATION_ATTRIBUTES));
     }
+
+    /**
+     * @see Vibrator#vibrate(int, String, VibrationEffect, String, VibrationAttributes)
+     */
+    public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe,
+            String reason, @NonNull VibrationAttributes attributes) {
+        if (!hasVibrator()) {
+            return;
+        }
+        mExecutor.execute(() -> mVibrator.vibrate(uid, opPkg, vibe, reason, attributes));
+    }
+
+    /**
+     * @see Vibrator#vibrate(VibrationEffect, AudioAttributes)
+     */
+    public void vibrate(@NonNull VibrationEffect effect, @NonNull AudioAttributes attributes) {
+        if (!hasVibrator()) {
+            return;
+        }
+        mExecutor.execute(() -> mVibrator.vibrate(effect, attributes));
+    }
+
+    /**
+     * @see Vibrator#vibrate(VibrationEffect)
+     */
+    public void vibrate(@NotNull VibrationEffect effect) {
+        if (!hasVibrator()) {
+            return;
+        }
+        mExecutor.execute(() -> mVibrator.vibrate(effect));
+    }
+
+    /**
+     * @see Vibrator#hasVibrator()
+     */
+    public boolean hasVibrator() {
+        return mVibrator != null && mVibrator.hasVibrator();
+    }
+
+    /**
+     * @see Vibrator#cancel()
+     */
+    public void cancel() {
+        if (!hasVibrator()) {
+            return;
+        }
+        mExecutor.execute(mVibrator::cancel);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 120c722..74c97fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -359,6 +359,13 @@
     private void buildList() {
         Trace.beginSection("ShadeListBuilder.buildList");
         mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
+
+        if (!mNotifStabilityManager.isPipelineRunAllowed()) {
+            mLogger.logPipelineRunSuppressed();
+            Trace.endSection();
+            return;
+        }
+
         mPipelineState.setState(STATE_BUILD_STARTED);
 
         // Step 1: Reset notification states
@@ -667,6 +674,12 @@
         //  having its summary promoted, regardless of how many children it has
         Set<String> groupsWithChildrenLostToStability =
                 getGroupsWithChildrenLostToStability(shadeList);
+        // Like groups which lost a child to stability, any group which lost a child to promotion
+        //  is exempt from having its summary promoted when it has no attached children.
+        Set<String> groupsWithChildrenLostToPromotionOrStability =
+                getGroupsWithChildrenLostToPromotion(shadeList);
+        groupsWithChildrenLostToPromotionOrStability.addAll(groupsWithChildrenLostToStability);
+
         for (int i = 0; i < shadeList.size(); i++) {
             final ListEntry tle = shadeList.get(i);
 
@@ -676,9 +689,9 @@
                 final boolean hasSummary = group.getSummary() != null;
 
                 if (hasSummary && children.size() == 0) {
-                    if (groupsWithChildrenLostToStability.contains(group.getKey())) {
-                        // This group lost a child on this run to stability, so it is exempt from
-                        //  having its summary promoted to the top level, so prune it.
+                    if (groupsWithChildrenLostToPromotionOrStability.contains(group.getKey())) {
+                        // This group lost a child on this run to promotion or stability, so it is
+                        //  exempt from having its summary promoted to the top level, so prune it.
                         //  It has no children, so it will just vanish.
                         pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
                     } else {
@@ -819,6 +832,26 @@
     }
 
     /**
+     * Collect the keys of any groups which have already lost a child to a {@link NotifPromoter}
+     * this run.
+     *
+     * These groups will be exempt from appearing without any children.
+     */
+    @NonNull
+    private Set<String> getGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList) {
+        ArraySet<String> groupsWithChildrenLostToPromotion = new ArraySet<>();
+        for (int i = 0; i < shadeList.size(); i++) {
+            final ListEntry tle = shadeList.get(i);
+            if (tle.getAttachState().getPromoter() != null) {
+                // This top-level-entry was part of a group, but was promoted out of it.
+                final String groupKey = tle.getRepresentativeEntry().getSbn().getGroupKey();
+                groupsWithChildrenLostToPromotion.add(groupKey);
+            }
+        }
+        return groupsWithChildrenLostToPromotion;
+    }
+
+    /**
      * If a ListEntry was added to the shade list and then later removed (e.g. because it was a
      * group that was broken up), this method will erase any bookkeeping traces of that addition
      * and/or check that they were already erased.
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 0bf21af..0311324 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
@@ -15,9 +15,13 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
+import android.app.Notification
+import android.app.Notification.GROUP_ALERT_SUMMARY
+import android.util.ArrayMap
 import android.util.ArraySet
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -29,13 +33,13 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.dagger.IncomingHeader
-import com.android.systemui.statusbar.notification.interruption.HeadsUpController
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
 import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 
 /**
@@ -54,27 +58,283 @@
  */
 @CoordinatorScope
 class HeadsUpCoordinator @Inject constructor(
+    private val mLogger: HeadsUpCoordinatorLogger,
+    private val mSystemClock: SystemClock,
     private val mHeadsUpManager: HeadsUpManager,
     private val mHeadsUpViewBinder: HeadsUpViewBinder,
     private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
     private val mRemoteInputManager: NotificationRemoteInputManager,
     @IncomingHeader private val mIncomingHeaderController: NodeController,
-    @Main private val mExecutor: DelayableExecutor
+    @Main private val mExecutor: DelayableExecutor,
 ) : Coordinator {
+    private val mEntriesBindingUntil = ArrayMap<String, Long>()
     private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
+    private lateinit var mNotifPipeline: NotifPipeline
+    private var mNow: Long = -1
 
     // notifs we've extended the lifetime for
     private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
 
     override fun attach(pipeline: NotifPipeline) {
+        mNotifPipeline = pipeline
         mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
         pipeline.addCollectionListener(mNotifCollectionListener)
+        pipeline.addOnBeforeTransformGroupsListener(::onBeforeTransformGroups)
+        pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
         pipeline.addPromoter(mNotifPromoter)
         pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
     }
 
     private fun onHeadsUpViewBound(entry: NotificationEntry) {
         mHeadsUpManager.showNotification(entry)
+        mEntriesBindingUntil.remove(entry.key)
+    }
+
+    /**
+     * Once the pipeline starts running, we can look through posted entries and quickly process
+     * any that don't have groups, and thus will never gave a group alert edge case.
+     */
+    fun onBeforeTransformGroups(list: List<ListEntry>) {
+        mNow = mSystemClock.currentTimeMillis()
+        if (mPostedEntries.isEmpty()) {
+            return
+        }
+        // Process all non-group adds/updates
+        mPostedEntries.values.toList().forEach { posted ->
+            if (!posted.entry.sbn.isGroup) {
+                handlePostedEntry(posted, "non-group")
+                mPostedEntries.remove(posted.key)
+            }
+        }
+    }
+
+    /**
+     * Once we have a nearly final shade list (not including what's pruned for inflation reasons),
+     * we know that stability and [NotifPromoter]s have been applied, so we can use the location of
+     * notifications in this list to determine what kind of group alert behavior should happen.
+     */
+    fun onBeforeFinalizeFilter(list: List<ListEntry>) {
+        // Nothing to do if there are no other adds/updates
+        if (mPostedEntries.isEmpty()) {
+            return
+        }
+        // Calculate a bunch of information about the logical group and the locations of group
+        // entries in the nearly-finalized shade list.  These may be used in the per-group loop.
+        val postedEntriesByGroup = mPostedEntries.values.groupBy { it.entry.sbn.groupKey }
+        val logicalMembersByGroup = mNotifPipeline.allNotifs.asSequence()
+            .filter { postedEntriesByGroup.contains(it.sbn.groupKey) }
+            .groupBy { it.sbn.groupKey }
+        val groupLocationsByKey: Map<String, GroupLocation> by lazy { getGroupLocationsByKey(list) }
+        mLogger.logEvaluatingGroups(postedEntriesByGroup.size)
+        // For each group, determine which notification(s) for a group should alert.
+        postedEntriesByGroup.forEach { (groupKey, postedEntries) ->
+            // get and classify the logical members
+            val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList()
+            val logicalSummary = logicalMembers.find { it.sbn.notification.isGroupSummary }
+
+            // Report the start of this group's evaluation
+            mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size)
+
+            // If there is no logical summary, then there is no alert to transfer
+            if (logicalSummary == null) {
+                postedEntries.forEach { handlePostedEntry(it, "logical-summary-missing") }
+                return@forEach
+            }
+
+            // If summary isn't wanted to be heads up, then there is no alert to transfer
+            if (!isGoingToShowHunStrict(logicalSummary)) {
+                postedEntries.forEach { handlePostedEntry(it, "logical-summary-not-alerting") }
+                return@forEach
+            }
+
+            // The group is alerting! Overall goals:
+            //  - Maybe transfer its alert to a child
+            //  - Also let any/all newly alerting children still alert
+            var childToReceiveParentAlert: NotificationEntry?
+            var targetType = "undefined"
+
+            // If the parent is alerting, always look at the posted notification with the newest
+            // 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive the
+            // parent's alert.
+            childToReceiveParentAlert =
+                findAlertOverride(postedEntries, groupLocationsByKey::getLocation)
+            if (childToReceiveParentAlert != null) {
+                targetType = "alertOverride"
+            }
+
+            // If the summary is Detached and we have not picked a receiver of the alert, then we
+            // need to look for the best child to alert in place of the summary.
+            val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key)
+            if (!isSummaryAttached && childToReceiveParentAlert == null) {
+                childToReceiveParentAlert =
+                    findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation)
+                if (childToReceiveParentAlert != null) {
+                    targetType = "bestChild"
+                }
+            }
+
+            // If there is no child to receive the parent alert, then just handle the posted entries
+            // and return.
+            if (childToReceiveParentAlert == null) {
+                postedEntries.forEach { handlePostedEntry(it, "no-transfer-target") }
+                return@forEach
+            }
+
+            // At this point we just need to initiate the transfer
+            val summaryUpdate = mPostedEntries[logicalSummary.key]
+
+            // If the summary was not attached, then remove the alert from the detached summary.
+            // Otherwise we can simply ignore its posted update.
+            if (!isSummaryAttached) {
+                val summaryUpdateForRemoval = summaryUpdate?.also {
+                    it.shouldHeadsUpEver = false
+                } ?: PostedEntry(logicalSummary,
+                    wasAdded = false,
+                    wasUpdated = false,
+                    shouldHeadsUpEver = false,
+                    shouldHeadsUpAgain = false,
+                    isAlerting = mHeadsUpManager.isAlerting(logicalSummary.key),
+                    isBinding = isEntryBinding(logicalSummary),
+                )
+                // If we transfer the alert and the summary isn't even attached, that means we
+                // should ensure the summary is no longer alerting, so we remove it here.
+                handlePostedEntry(summaryUpdateForRemoval, "detached-summary-remove-alert")
+            } else if (summaryUpdate!=null) {
+                mLogger.logPostedEntryWillNotEvaluate(summaryUpdate, "attached-summary-transferred")
+            }
+
+            // Handle all posted entries -- if the child receiving the parent's alert is in the
+            // list, then set its flags to ensure it alerts.
+            var didAlertChildToReceiveParentAlert = false
+            postedEntries.asSequence()
+                .filter { it.key != logicalSummary.key }
+                .forEach { postedEntry ->
+                    if (childToReceiveParentAlert.key == postedEntry.key) {
+                        // Update the child's posted update so that it
+                        postedEntry.shouldHeadsUpEver = true
+                        postedEntry.shouldHeadsUpAgain = true
+                        handlePostedEntry(postedEntry, "child-alert-transfer-target-$targetType")
+                        didAlertChildToReceiveParentAlert = true
+                    } else {
+                        handlePostedEntry(postedEntry, "child-alert-non-target")
+                    }
+                }
+
+            // If the child receiving the alert was not updated on this tick (which can happen in a
+            // standard alert transfer scenario), then construct an update so that we can apply it.
+            if (!didAlertChildToReceiveParentAlert) {
+                val posted = PostedEntry(
+                    childToReceiveParentAlert,
+                    wasAdded = false,
+                    wasUpdated = false,
+                    shouldHeadsUpEver = true,
+                    shouldHeadsUpAgain = true,
+                    isAlerting = mHeadsUpManager.isAlerting(childToReceiveParentAlert.key),
+                    isBinding = isEntryBinding(childToReceiveParentAlert),
+                )
+                handlePostedEntry(posted, "non-posted-child-alert-transfer-target-$targetType")
+            }
+        }
+        // After this method runs, all posted entries should have been handled (or skipped).
+        mPostedEntries.clear()
+    }
+
+    /**
+     * Find the posted child with the newest when, and return it if it is isolated and has
+     * GROUP_ALERT_SUMMARY so that it can be alerted.
+     */
+    private fun findAlertOverride(
+        postedEntries: List<PostedEntry>,
+        locationLookupByKey: (String) -> GroupLocation,
+    ): NotificationEntry? = postedEntries.asSequence()
+        .filter { posted -> !posted.entry.sbn.notification.isGroupSummary }
+        .sortedBy { posted -> -posted.entry.sbn.notification.`when` }
+        .firstOrNull()
+        ?.let { posted ->
+            posted.entry.takeIf { entry ->
+                locationLookupByKey(entry.key) == GroupLocation.Isolated
+                        && entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY
+            }
+        }
+
+    /**
+     * Of children which are attached, look for the child to receive the notification:
+     * First prefer children which were updated, then looking for the ones with the newest 'when'
+     */
+    private fun findBestTransferChild(
+        logicalMembers: List<NotificationEntry>,
+        locationLookupByKey: (String) -> GroupLocation,
+    ): NotificationEntry? = logicalMembers.asSequence()
+        .filter { !it.sbn.notification.isGroupSummary }
+        .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
+        .sortedWith(compareBy(
+            { !mPostedEntries.contains(it.key) },
+            { -it.sbn.notification.`when` },
+        ))
+        .firstOrNull()
+
+    private fun getGroupLocationsByKey(list: List<ListEntry>): Map<String, GroupLocation> =
+        mutableMapOf<String, GroupLocation>().also { map ->
+            list.forEach { topLevelEntry ->
+                when (topLevelEntry) {
+                    is NotificationEntry -> map[topLevelEntry.key] = GroupLocation.Isolated
+                    is GroupEntry -> {
+                        topLevelEntry.summary?.let { summary ->
+                            map[summary.key] = GroupLocation.Summary
+                        }
+                        topLevelEntry.children.forEach { child ->
+                            map[child.key] = GroupLocation.Child
+                        }
+                    }
+                    else -> error("unhandled type $topLevelEntry")
+                }
+            }
+        }
+
+    private val mPostedEntries = LinkedHashMap<String, PostedEntry>()
+
+    fun handlePostedEntry(posted: PostedEntry, scenario: String) {
+        mLogger.logPostedEntryWillEvaluate(posted, scenario)
+        if (posted.wasAdded) {
+            if (posted.shouldHeadsUpEver) {
+                bindForAsyncHeadsUp(posted)
+            }
+        } else {
+            if (posted.isHeadsUpAlready) {
+                // NOTE: This might be because we're alerting (i.e. tracked by HeadsUpManager) OR
+                // it could be because we're binding, and that will affect the next step.
+                if (posted.shouldHeadsUpEver) {
+                    // If alerting, we need to post an update.  Otherwise we're still binding,
+                    // and we can just let that finish.
+                    if (posted.isAlerting) {
+                        mHeadsUpManager.updateNotification(posted.key, posted.shouldHeadsUpAgain)
+                    }
+                } else {
+                    if (posted.isAlerting) {
+                        // We don't want this to be interrupting anymore, let's remove it
+                        mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+                    } else {
+                        // Don't let the bind finish
+                        cancelHeadsUpBind(posted.entry)
+                    }
+                }
+            } else if (posted.shouldHeadsUpEver && posted.shouldHeadsUpAgain) {
+                // This notification was updated to be heads up, show it!
+                bindForAsyncHeadsUp(posted)
+            }
+        }
+    }
+
+    private fun cancelHeadsUpBind(entry: NotificationEntry) {
+        mEntriesBindingUntil.remove(entry.key)
+        mHeadsUpViewBinder.abortBindCallback(entry)
+    }
+
+    private fun bindForAsyncHeadsUp(posted: PostedEntry) {
+        // TODO: Add a guarantee to bindHeadsUpView of some kind of callback if the bind is
+        //  cancelled so that we don't need to have this sad timeout hack.
+        mEntriesBindingUntil[posted.key] = mNow + BIND_TIMEOUT
+        mHeadsUpViewBinder.bindHeadsUpView(posted.entry, this::onHeadsUpViewBound)
     }
 
     private val mNotifCollectionListener = object : NotifCollectionListener {
@@ -82,9 +342,17 @@
          * Notification was just added and if it should heads up, bind the view and then show it.
          */
         override fun onEntryAdded(entry: NotificationEntry) {
-            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
-                mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
-            }
+            // shouldHeadsUp includes check for whether this notification should be filtered
+            val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+            mPostedEntries[entry.key] = PostedEntry(
+                entry,
+                wasAdded = true,
+                wasUpdated = false,
+                shouldHeadsUpEver = shouldHeadsUpEver,
+                shouldHeadsUpAgain = true,
+                isAlerting = false,
+                isBinding = false,
+            )
         }
 
         /**
@@ -93,22 +361,26 @@
          * up again.
          */
         override fun onEntryUpdated(entry: NotificationEntry) {
-            val hunAgain = HeadsUpController.alertAgain(entry, entry.sbn.notification)
-            // includes check for whether this notification should be filtered:
-            val shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
-            val wasHeadsUp = mHeadsUpManager.isAlerting(entry.key)
-            if (wasHeadsUp) {
-                if (shouldHeadsUp) {
-                    mHeadsUpManager.updateNotification(entry.key, hunAgain)
-                } else {
-                    // We don't want this to be interrupting anymore, let's remove it
-                    mHeadsUpManager.removeNotification(
-                        entry.key, false /* removeImmediately */
-                    )
-                }
-            } else if (shouldHeadsUp && hunAgain) {
-                // This notification was updated to be heads up, show it!
-                mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+            val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+            val shouldHeadsUpAgain = shouldHunAgain(entry)
+            val isAlerting = mHeadsUpManager.isAlerting(entry.key)
+            val isBinding = isEntryBinding(entry)
+            mPostedEntries.compute(entry.key) { _, value ->
+                value?.also { update ->
+                    update.wasUpdated = true
+                    update.shouldHeadsUpEver = update.shouldHeadsUpEver || shouldHeadsUpEver
+                    update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain
+                    update.isAlerting = isAlerting
+                    update.isBinding = isBinding
+                } ?: PostedEntry(
+                    entry,
+                    wasAdded = false,
+                    wasUpdated = true,
+                    shouldHeadsUpEver = shouldHeadsUpEver,
+                    shouldHeadsUpAgain = shouldHeadsUpAgain,
+                    isAlerting = isAlerting,
+                    isBinding = isBinding,
+                )
             }
         }
 
@@ -116,8 +388,12 @@
          * Stop alerting HUNs that are removed from the notification collection
          */
         override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+            mPostedEntries.remove(entry.key)
+            cancelHeadsUpBind(entry)
             val entryKey = entry.key
             if (mHeadsUpManager.isAlerting(entryKey)) {
+                // TODO: This should probably know the RemoteInputCoordinator's conditions,
+                //  or otherwise reference that coordinator's state, rather than replicate its logic
                 val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
                         !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
                 mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
@@ -129,6 +405,14 @@
         }
     }
 
+    /**
+     * Checks whether an update for a notification warrants an alert for the user.
+     */
+    private fun shouldHunAgain(entry: NotificationEntry): Boolean {
+        return (!entry.hasInterrupted() ||
+                (entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0)
+    }
+
     private val mLifetimeExtender = object : NotifLifetimeExtender {
         override fun getName() = TAG
 
@@ -164,11 +448,13 @@
 
     private val mNotifPromoter = object : NotifPromoter(TAG) {
         override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
-            isCurrentlyShowingHun(entry)
+            isGoingToShowHunNoRetract(entry)
     }
 
     val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
-        override fun isInSection(entry: ListEntry): Boolean = isCurrentlyShowingHun(entry)
+        override fun isInSection(entry: ListEntry): Boolean =
+            // TODO: This check won't notice if a child of the group is going to HUN...
+            isGoingToShowHunNoRetract(entry)
 
         override fun getHeaderNodeController(): NodeController? =
             // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
@@ -186,7 +472,34 @@
 
     private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
 
-    private fun isCurrentlyShowingHun(entry: ListEntry) = mHeadsUpManager.isAlerting(entry.key)
+    private fun isEntryBinding(entry: ListEntry): Boolean {
+        val bindingUntil = mEntriesBindingUntil[entry.key]
+        return bindingUntil != null && bindingUntil >= mNow
+    }
+
+    /**
+     * Whether the notification is already alerting or binding so that it can imminently alert
+     */
+    private fun isAttemptingToShowHun(entry: ListEntry) =
+        mHeadsUpManager.isAlerting(entry.key) || isEntryBinding(entry)
+
+    /**
+     * Whether the notification is already alerting/binding per [isAttemptingToShowHun] OR if it
+     * has been updated so that it should alert this update.  This method is permissive because it
+     * returns `true` even if the update would (in isolation of its group) cause the alert to be
+     * retracted.  This is important for not retracting transferred group alerts.
+     */
+    private fun isGoingToShowHunNoRetract(entry: ListEntry) =
+        mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
+
+    /**
+     * If the notification has been updated, then whether it should HUN in isolation, otherwise
+     * defers to the already alerting/binding state of [isAttemptingToShowHun].  This method is
+     * strict because any update which would revoke the alert supersedes the current
+     * alerting/binding state.
+     */
+    private fun isGoingToShowHunStrict(entry: ListEntry) =
+        mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
 
     private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
         if (mNotifsExtendingLifetime.remove(entry)) {
@@ -196,5 +509,29 @@
 
     companion object {
         private const val TAG = "HeadsUpCoordinator"
+        private const val BIND_TIMEOUT = 1000L
     }
-}
\ No newline at end of file
+
+    data class PostedEntry(
+        val entry: NotificationEntry,
+        val wasAdded: Boolean,
+        var wasUpdated: Boolean,
+        var shouldHeadsUpEver: Boolean,
+        var shouldHeadsUpAgain: Boolean,
+        var isAlerting: Boolean,
+        var isBinding: Boolean,
+    ) {
+        val key = entry.key
+        val isHeadsUpAlready: Boolean
+            get() = isAlerting || isBinding
+        val calculateShouldBeHeadsUpStrict: Boolean
+            get() = shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain || isHeadsUpAlready)
+        val calculateShouldBeHeadsUpNoRetract: Boolean
+            get() = isHeadsUpAlready || (shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain))
+    }
+}
+
+private enum class GroupLocation { Detached, Isolated, Summary, Child }
+
+private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
+    getOrDefault(key, GroupLocation.Detached)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
new file mode 100644
index 0000000..204a494
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -0,0 +1,62 @@
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.Log
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import javax.inject.Inject
+
+private const val TAG = "HeadsUpCoordinator"
+
+class HeadsUpCoordinatorLogger constructor(
+    private val buffer: LogBuffer,
+    private val verbose: Boolean,
+) {
+    @Inject
+    constructor(@NotificationHeadsUpLog buffer: LogBuffer) :
+            this(buffer, Log.isLoggable(TAG, Log.VERBOSE))
+
+    fun logPostedEntryWillEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) {
+        if (!verbose) return
+        buffer.log(TAG, LogLevel.VERBOSE, {
+            str1 = posted.key
+            str2 = reason
+            bool1 = posted.shouldHeadsUpEver
+            bool2 = posted.shouldHeadsUpAgain
+        }, {
+            "will evaluate posted entry $str1:" +
+                    " reason=$str2 shouldHeadsUpEver=$bool1 shouldHeadsUpAgain=$bool2"
+        })
+    }
+
+    fun logPostedEntryWillNotEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) {
+        if (!verbose) return
+        buffer.log(TAG, LogLevel.VERBOSE, {
+            str1 = posted.key
+            str2 = reason
+        }, {
+            "will not evaluate posted entry $str1: reason=$str2"
+        })
+    }
+
+    fun logEvaluatingGroups(numGroups: Int) {
+        if (!verbose) return
+        buffer.log(TAG, LogLevel.VERBOSE, {
+            int1 = numGroups
+        }, {
+            "evaluating groups for alert transfer: $int1"
+        })
+    }
+
+    fun logEvaluatingGroup(groupKey: String, numPostedEntries: Int, logicalGroupSize: Int) {
+        if (!verbose) return
+        buffer.log(TAG, LogLevel.VERBOSE, {
+            str1 = groupKey
+            int1 = numPostedEntries
+            int2 = logicalGroupSize
+        }, {
+            "evaluating group for alert transfer: $str1" +
+                    " numPostedEntries=$int1 logicalGroupSize=$int2"
+        })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 327876c..fcc2b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -56,17 +57,23 @@
  */
 // TODO(b/204468557): Move to @CoordinatorScope
 @SysUISingleton
-public class VisualStabilityCoordinator implements Coordinator, Dumpable {
+public class VisualStabilityCoordinator implements Coordinator, Dumpable,
+        NotifPanelEventSource.Callbacks {
     private final DelayableExecutor mDelayableExecutor;
-    private final WakefulnessLifecycle mWakefulnessLifecycle;
-    private final StatusBarStateController mStatusBarStateController;
     private final HeadsUpManager mHeadsUpManager;
+    private final NotifPanelEventSource mNotifPanelEventSource;
+    private final StatusBarStateController mStatusBarStateController;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
 
     private boolean mScreenOn;
     private boolean mPanelExpanded;
     private boolean mPulsing;
+    private boolean mNotifPanelCollapsing;
+    private boolean mNotifPanelLaunchingActivity;
 
+    private boolean mPipelineRunAllowed;
     private boolean mReorderingAllowed;
+    private boolean mIsSuppressingPipelineRun = false;
     private boolean mIsSuppressingGroupChange = false;
     private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
     private boolean mIsSuppressingEntryReorder = false;
@@ -81,16 +88,17 @@
 
     @Inject
     public VisualStabilityCoordinator(
+            DelayableExecutor delayableExecutor,
             DumpManager dumpManager,
             HeadsUpManager headsUpManager,
-            WakefulnessLifecycle wakefulnessLifecycle,
+            NotifPanelEventSource notifPanelEventSource,
             StatusBarStateController statusBarStateController,
-            DelayableExecutor delayableExecutor
-    ) {
+            WakefulnessLifecycle wakefulnessLifecycle) {
         mHeadsUpManager = headsUpManager;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
         mDelayableExecutor = delayableExecutor;
+        mNotifPanelEventSource = notifPanelEventSource;
 
         dumpManager.registerDumpable(this);
     }
@@ -103,6 +111,7 @@
 
         mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
         mPulsing = mStatusBarStateController.isPulsing();
+        mNotifPanelEventSource.registerCallbacks(this);
 
         pipeline.setVisualStabilityManager(mNotifStabilityManager);
     }
@@ -112,12 +121,19 @@
             new NotifStabilityManager("VisualStabilityCoordinator") {
                 @Override
                 public void onBeginRun() {
+                    mIsSuppressingPipelineRun = false;
                     mIsSuppressingGroupChange = false;
                     mEntriesWithSuppressedSectionChange.clear();
                     mIsSuppressingEntryReorder = false;
                 }
 
                 @Override
+                public boolean isPipelineRunAllowed() {
+                    mIsSuppressingPipelineRun |= !mPipelineRunAllowed;
+                    return mPipelineRunAllowed;
+                }
+
+                @Override
                 public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
                     final boolean isGroupChangeAllowedForEntry =
                             mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
@@ -154,9 +170,12 @@
             };
 
     private void updateAllowedStates() {
+        mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity();
         mReorderingAllowed = isReorderingAllowed();
-        if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange()
-                || mIsSuppressingEntryReorder)) {
+        if ((mPipelineRunAllowed && mIsSuppressingPipelineRun)
+                || (mReorderingAllowed && (mIsSuppressingGroupChange
+                        || isSuppressingSectionChange()
+                        || mIsSuppressingEntryReorder))) {
             mNotifStabilityManager.invalidateList();
         }
     }
@@ -165,6 +184,10 @@
         return !mEntriesWithSuppressedSectionChange.isEmpty();
     }
 
+    private boolean isPanelCollapsingOrLaunchingActivity() {
+        return mNotifPanelCollapsing || mNotifPanelLaunchingActivity;
+    }
+
     private boolean isReorderingAllowed() {
         return (!mScreenOn || !mPanelExpanded) && !mPulsing;
     }
@@ -248,4 +271,16 @@
             pw.println("  " + key);
         }
     }
+
+    @Override
+    public void onPanelCollapsingChanged(boolean isCollapsing) {
+        mNotifPanelCollapsing = isCollapsing;
+        updateAllowedStates();
+    }
+
+    @Override
+    public void onLaunchingActivityChanged(boolean isLaunchingActivity) {
+        mNotifPanelLaunchingActivity = isLaunchingActivity;
+        updateAllowedStates();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index ba3e855..f8bf85f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -306,6 +306,9 @@
             }
         }
     }
+
+    fun logPipelineRunSuppressed() =
+            buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." }
 }
 
 private const val TAG = "ShadeListBuilder"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
index 60f557c..4464498 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -27,6 +27,16 @@
  */
 abstract class NotifStabilityManager protected constructor(name: String) :
     Pluggable<NotifStabilityManager>(name) {
+
+    /**
+     * Called prior to running the pipeline to suppress any visual changes. Ex: collapse animation
+     * is playing, moving stuff around simultaneously will look janky.
+     *
+     * Note: this is invoked *before* [onBeginRun], so that implementors can reference state
+     * maintained from a previous run.
+     */
+    abstract fun isPipelineRunAllowed(): Boolean
+
     /**
      * Called at the beginning of every pipeline run to perform any necessary cleanup from the
      * previous run.
@@ -76,6 +86,7 @@
 
 /** The default, no-op instance of the stability manager which always allows all changes */
 object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabilityManager") {
+    override fun isPipelineRunAllowed(): Boolean = true
     override fun onBeginRun() {}
     override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
     override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
new file mode 100644
index 0000000..920d3c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.util.ListenerSet
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoSet
+
+/** Provides certain notification panel events.  */
+interface NotifPanelEventSource {
+
+    /** Registers callbacks to be invoked when notification panel events occur.  */
+    fun registerCallbacks(callbacks: Callbacks)
+
+    /** Unregisters callbacks previously registered via [.registerCallbacks]  */
+    fun unregisterCallbacks(callbacks: Callbacks)
+
+    /** Callbacks for certain notification panel events. */
+    interface Callbacks {
+
+        /** Invoked when the notification panel starts or stops collapsing. */
+        fun onPanelCollapsingChanged(isCollapsing: Boolean)
+
+        /**
+         * Invoked when the notification panel starts or stops launching an [android.app.Activity].
+         */
+        fun onLaunchingActivityChanged(isLaunchingActivity: Boolean)
+    }
+}
+
+@Module
+abstract class NotifPanelEventSourceModule {
+
+    @Binds
+    @SysUISingleton
+    abstract fun bindEventSource(manager: NotifPanelEventSourceManager): NotifPanelEventSource
+
+    @Module
+    companion object {
+        @JvmStatic
+        @Provides
+        fun provideManager(): NotifPanelEventSourceManager = NotifPanelEventSourceManagerImpl()
+    }
+}
+
+@Module
+object StatusBarNotifPanelEventSourceModule {
+    @JvmStatic
+    @Provides
+    @IntoSet
+    @StatusBarScope
+    fun bindStartable(
+        manager: NotifPanelEventSourceManager,
+        notifPanelController: NotificationPanelViewController
+    ): StatusBarComponent.Startable =
+            EventSourceStatusBarStartableImpl(manager, notifPanelController)
+}
+
+/**
+ * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that
+ * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves
+ * come from [NotificationPanelViewController] in [StatusBarScope].
+ */
+interface NotifPanelEventSourceManager : NotifPanelEventSource {
+    var eventSource: NotifPanelEventSource?
+}
+
+private class NotifPanelEventSourceManagerImpl
+    : NotifPanelEventSourceManager, NotifPanelEventSource.Callbacks {
+
+    private val callbackSet = ListenerSet<NotifPanelEventSource.Callbacks>()
+
+    override var eventSource: NotifPanelEventSource? = null
+        set(value) {
+            field?.unregisterCallbacks(this)
+            value?.registerCallbacks(this)
+            field = value
+        }
+
+    override fun registerCallbacks(callbacks: NotifPanelEventSource.Callbacks) {
+        callbackSet.addIfAbsent(callbacks)
+    }
+
+    override fun unregisterCallbacks(callbacks: NotifPanelEventSource.Callbacks) {
+        callbackSet.remove(callbacks)
+    }
+
+    override fun onPanelCollapsingChanged(isCollapsing: Boolean) {
+        callbackSet.forEach { it.onPanelCollapsingChanged(isCollapsing) }
+    }
+
+    override fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {
+        callbackSet.forEach { it.onLaunchingActivityChanged(isLaunchingActivity) }
+    }
+}
+
+private class EventSourceStatusBarStartableImpl(
+    private val manager: NotifPanelEventSourceManager,
+    private val notifPanelController: NotificationPanelViewController
+) : StatusBarComponent.Startable {
+
+    override fun start() {
+        manager.eventSource = notifPanelController
+    }
+
+    override fun stop() {
+        manager.eventSource = null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 05c40b2..45a9092 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -67,6 +67,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSourceModule;
 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;
@@ -101,8 +102,9 @@
  * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
  */
 @Module(includes = {
+        CoordinatorsModule.class,
+        NotifPanelEventSourceModule.class,
         NotificationSectionHeadersModule.class,
-        CoordinatorsModule.class
 })
 public interface NotificationsModule {
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index ffec367..27610b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -50,15 +50,17 @@
     private final NotificationMessagingUtil mNotificationMessagingUtil;
     private final Map<NotificationEntry, CancellationSignal> mOngoingBindCallbacks =
             new ArrayMap<>();
+    private final HeadsUpViewBinderLogger mLogger;
 
     private NotificationPresenter mNotificationPresenter;
 
     @Inject
     HeadsUpViewBinder(
             NotificationMessagingUtil notificationMessagingUtil,
-            RowContentBindStage bindStage) {
+            RowContentBindStage bindStage, HeadsUpViewBinderLogger logger) {
         mNotificationMessagingUtil = notificationMessagingUtil;
         mStage = bindStage;
+        mLogger = logger;
     }
 
     /**
@@ -81,12 +83,14 @@
         params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
         CancellationSignal signal = mStage.requestRebind(entry, en -> {
+            mLogger.entryBoundSuccessfully(entry.getKey());
             en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
             if (callback != null) {
                 callback.onBindFinished(en);
             }
         });
         abortBindCallback(entry);
+        mLogger.startBindingHun(entry.getKey());
         mOngoingBindCallbacks.put(entry, signal);
     }
 
@@ -97,6 +101,7 @@
     public void abortBindCallback(NotificationEntry entry) {
         CancellationSignal ongoingBindCallback = mOngoingBindCallbacks.remove(entry);
         if (ongoingBindCallback != null) {
+            mLogger.currentOngoingBindingAborted(entry.getKey());
             ongoingBindCallback.cancel();
         }
     }
@@ -107,6 +112,7 @@
     public void unbindHeadsUpView(NotificationEntry entry) {
         abortBindCallback(entry);
         mStage.getStageParams(entry).markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
-        mStage.requestRebind(entry, null);
+        mLogger.entryContentViewMarkedFreeable(entry.getKey());
+        mStage.requestRebind(entry, e -> mLogger.entryUnbound(e.getKey()));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
new file mode 100644
index 0000000..06651f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -0,0 +1,49 @@
+package com.android.systemui.statusbar.notification.interruption
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import javax.inject.Inject
+
+class HeadsUpViewBinderLogger @Inject constructor(@NotificationHeadsUpLog val buffer: LogBuffer) {
+    fun startBindingHun(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "start binding heads up entry $str1 "
+        })
+    }
+
+    fun currentOngoingBindingAborted(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "aborted potential ongoing heads up entry binding $str1 "
+        })
+    }
+
+    fun entryBoundSuccessfully(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "heads up entry bound successfully $str1 "
+        })
+    }
+
+    fun entryUnbound(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "heads up entry unbound successfully $str1 "
+        })
+    }
+
+    fun entryContentViewMarkedFreeable(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "start unbinding heads up entry $str1 "
+        })
+    }
+}
+const val TAG = "HeadsUpViewBinder"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 7dc2e19..ce3e27c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -565,6 +565,10 @@
         }
     }
 
+    public float getDozeAmount() {
+        return mDozeAmount;
+    }
+
     /**
      * Is the device fully awake, which is different from not tark at all when there are pulsing
      * notifications.
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
deleted file mode 100644
index bd5b7d7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-/**
- * Root view to insert Lock screen media controls into the notification stack.
- */
-public class MediaContainerView extends ExpandableView {
-
-    public MediaContainerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public long performRemoveAnimation(long duration, long delay, float translationDirection,
-            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
-            AnimatorListenerAdapter animationListener) {
-        return 0;
-    }
-
-    @Override
-    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/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
new file mode 100644
index 0000000..b8f28b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.statusbar.notification.stack
+
+import android.animation.AnimatorListenerAdapter
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.RectF
+import android.util.AttributeSet
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.row.ExpandableView
+
+/**
+ * Root view to insert Lock screen media controls into the notification stack.
+ */
+class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) {
+
+    var cornerRadius = 0f
+    var clipHeight = 0
+    var clipRect = RectF()
+    var clipPath = Path()
+
+    init {
+        setWillNotDraw(false) // Run onDraw after invalidate.
+        updateResources()
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration?) {
+        super.onConfigurationChanged(newConfig)
+        updateResources()
+    }
+
+    private fun updateResources() {
+        cornerRadius = context.resources
+                .getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
+    }
+
+    public override fun updateClipping() {
+        if (clipHeight != actualHeight) {
+            clipHeight = actualHeight
+        }
+        invalidate()
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        val bounds = canvas.clipBounds
+        bounds.bottom = clipHeight
+        clipRect.set(bounds)
+
+        clipPath.reset()
+        clipPath.addRoundRect(clipRect, cornerRadius, cornerRadius, Path.Direction.CW)
+        canvas.clipPath(clipPath)
+    }
+
+
+    override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float,
+                                        isHeadsUpAnimation: Boolean, endLocation: Float,
+                                        onFinishedRunnable: Runnable?,
+                                        animationListener: AnimatorListenerAdapter?): Long {
+        return 0
+    }
+
+    override fun performAddAnimation(delay: Long, duration: Long, isHeadsUpAppear: Boolean,
+                                     onEnd: Runnable?) {
+        // No animation, it doesn't need it, this would be local
+    }
+}
\ No newline at end of file
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 4283343..9655a9c 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
@@ -202,6 +202,7 @@
     private int mBottomMargin;
     private int mBottomInset = 0;
     private float mQsExpansionFraction;
+    private final int mSplitShadeMinContentHeight;
 
     /**
      * The algorithm which calculates the properties for our children
@@ -305,7 +306,7 @@
             return true;
         }
     };
-
+    private NotificationStackScrollLogger mLogger;
     private StatusBar mStatusBar;
     private int[] mTempInt2 = new int[2];
     private boolean mGenerateChildOrderChangedEvent;
@@ -583,6 +584,8 @@
                 .getDefaultColor();
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
+        mSplitShadeMinContentHeight = res.getDimensionPixelSize(
+                R.dimen.nssl_split_shade_min_content_height);
         mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
                 minHeight, maxHeight);
         mExpandHelper.setEventSource(this);
@@ -658,6 +661,10 @@
         return 0f;
     }
 
+    protected void setLogger(NotificationStackScrollLogger logger) {
+        mLogger = logger;
+    }
+
     public float getNotificationSquishinessFraction() {
         return mStackScrollAlgorithm.getNotificationSquishinessFraction(mAmbientState);
     }
@@ -736,6 +743,21 @@
         }
     }
 
+    private void logHunSkippedForUnexpectedState(String key, boolean expected, boolean actual) {
+        if (mLogger == null) return;
+        mLogger.hunSkippedForUnexpectedState(key, expected, actual);
+    }
+
+    private void logHunAnimationSkipped(String key, String reason) {
+        if (mLogger == null) return;
+        mLogger.hunAnimationSkipped(key, reason);
+    }
+
+    private void logHunAnimationEventAdded(String key, int type) {
+        if (mLogger == null) return;
+        mLogger.hunAnimationEventAdded(key, type);
+    }
+
     private void onDrawDebug(Canvas canvas) {
         if (mDebugTextUsedYPositions == null) {
             mDebugTextUsedYPositions = new HashSet<>();
@@ -1254,8 +1276,7 @@
      * @param listenerNeedsAnimation does the listener need to animate?
      */
     private void updateStackPosition(boolean listenerNeedsAnimation) {
-        // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
-        float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+        final float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
                 + mAmbientState.getOverExpansion()
                 - getCurrentOverScrollAmount(false /* top */);
         final float fraction = mAmbientState.getExpansionFraction();
@@ -1265,15 +1286,31 @@
             mOnStackYChanged.accept(listenerNeedsAnimation);
         }
         if (mQsExpansionFraction <= 0) {
-            final float stackEndHeight = Math.max(0f,
-                    getHeight() - getEmptyBottomMargin() - mTopPadding);
-            mAmbientState.setStackEndHeight(stackEndHeight);
-            mAmbientState.setStackHeight(
-                    MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION,
-                            stackEndHeight, fraction));
+            final float endHeight = updateStackEndHeight(
+                    getHeight(), getEmptyBottomMargin(), mTopPadding);
+            updateStackHeight(endHeight, fraction);
         }
     }
 
+    public float updateStackEndHeight(float height, float bottomMargin, float topPadding) {
+        final float stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
+        mAmbientState.setStackEndHeight(stackEndHeight);
+        return stackEndHeight;
+    }
+
+    public void updateStackHeight(float endHeight, float fraction) {
+        // During the (AOD<=>LS) transition where dozeAmount is changing,
+        // apply dozeAmount to stack height instead of expansionFraction
+        // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
+        final float dozeAmount = mAmbientState.getDozeAmount();
+        if (0f < dozeAmount && dozeAmount < 1f) {
+            fraction = 1f - dozeAmount;
+        }
+        mAmbientState.setStackHeight(
+                MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
+                        endHeight, fraction));
+    }
+
     /**
      * Add a listener when the StackY changes. The argument signifies whether an animation is
      * needed.
@@ -3100,10 +3137,12 @@
     private void generateHeadsUpAnimationEvents() {
         for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
             ExpandableNotificationRow row = eventPair.first;
+            String key = row.getEntry().getKey();
             boolean isHeadsUp = eventPair.second;
             if (isHeadsUp != row.isHeadsUp()) {
                 // For cases where we have a heads up showing and appearing again we shouldn't
                 // do the animations at all.
+                logHunSkippedForUnexpectedState(key, isHeadsUp, row.isHeadsUp());
                 continue;
             }
             int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
@@ -3121,6 +3160,7 @@
                 if (row.isChildInGroup()) {
                     // We can otherwise get stuck in there if it was just isolated
                     row.setHeadsUpAnimatingAway(false);
+                    logHunAnimationSkipped(key, "row is child in group");
                     continue;
                 }
             } else {
@@ -3128,6 +3168,7 @@
                 if (viewState == null) {
                     // A view state was never generated for this view, so we don't need to animate
                     // this. This may happen with notification children.
+                    logHunAnimationSkipped(key, "row has no viewState");
                     continue;
                 }
                 if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
@@ -3151,6 +3192,7 @@
                         + " onBottom=" + onBottom
                         + " row=" + row.getEntry().getKey());
             }
+            logHunAnimationEventAdded(key, type);
         }
         mHeadsUpChangeAnimations.clear();
         mAddedHeadsUpChildren.clear();
@@ -3886,7 +3928,17 @@
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     int getEmptyBottomMargin() {
-        return Math.max(mMaxLayoutHeight - mContentHeight, 0);
+        int contentHeight;
+        if (mShouldUseSplitNotificationShade) {
+            // When in split shade and there are no notifications, the height can be too low, as
+            // it is based on notifications bottom, which is lower on split shade.
+            // Here we prefer to use at least a minimum height defined for split shade.
+            // Otherwise the expansion motion is too fast.
+            contentHeight = Math.max(mSplitShadeMinContentHeight, mContentHeight);
+        } else {
+            contentHeight = mContentHeight;
+        }
+        return Math.max(mMaxLayoutHeight - contentHeight, 0);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4699,6 +4751,8 @@
                 if (SPEW) {
                     Log.v(TAG, "generateHeadsUpAnimation: previous hun appear animation cancelled");
                 }
+                logHunAnimationSkipped(row.getEntry().getKey(),
+                        "previous hun appear animation cancelled");
                 return;
             }
             mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
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 51ce779..0d0e5e8 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
@@ -185,6 +185,7 @@
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final InteractionJankMonitor mJankMonitor;
     private final StackStateLogger mStackStateLogger;
+    private final NotificationStackScrollLogger mLogger;
 
     private NotificationStackScrollLayout mView;
     private boolean mFadeNotificationsOnDismiss;
@@ -662,8 +663,10 @@
             VisualStabilityManager visualStabilityManager,
             ShadeController shadeController,
             InteractionJankMonitor jankMonitor,
-            StackStateLogger stackLogger) {
+            StackStateLogger stackLogger,
+            NotificationStackScrollLogger logger) {
         mStackStateLogger = stackLogger;
+        mLogger = logger;
         mAllowLongPress = allowLongPress;
         mNotificationGutsManager = notificationGutsManager;
         mVisibilityProvider = visibilityProvider;
@@ -717,6 +720,7 @@
         mView = view;
         mView.setLogger(mStackStateLogger);
         mView.setController(this);
+        mView.setLogger(mLogger);
         mView.setTouchHandler(new TouchHandler());
         mView.setStatusBar(mStatusBar);
         mView.setClearAllAnimationListener(this::onAnimationEnd);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
new file mode 100644
index 0000000..04bf621
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.statusbar.notification.stack
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.*
+import javax.inject.Inject
+
+class NotificationStackScrollLogger @Inject constructor(
+    @NotificationHeadsUpLog private val buffer: LogBuffer
+) {
+    fun hunAnimationSkipped(key: String, reason: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+            str2 = reason
+        }, {
+            "heads up animation skipped: key: $str1 reason: $str2"
+        })
+    }
+    fun hunAnimationEventAdded(key: String, type: Int) {
+        val reason: String
+        reason = if (type == ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+            "HEADS_UP_DISAPPEAR"
+        } else if (type == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+            "HEADS_UP_DISAPPEAR_CLICK"
+        } else if (type == ANIMATION_TYPE_HEADS_UP_APPEAR) {
+            "HEADS_UP_APPEAR"
+        } else if (type == ANIMATION_TYPE_HEADS_UP_OTHER) {
+            "HEADS_UP_OTHER"
+        } else if (type == ANIMATION_TYPE_ADD) {
+            "ADD"
+        } else {
+            type.toString()
+        }
+        buffer.log(TAG, INFO, {
+            str1 = key
+            str2 = reason
+        }, {
+            "heads up animation added: $str1 with type $str2"
+        })
+    }
+
+    fun hunSkippedForUnexpectedState(key: String, expected: Boolean, actual: Boolean) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+            bool1 = expected
+            bool2 = actual
+        }, {
+            "HUN animation skipped for unexpected hun state: " +
+                    "key: $str1 expected: $bool1 actual: $bool2"
+        })
+    }
+}
+
+private const val TAG = "NotificationStackScroll"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 8d500fa..04d3e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.StatusBarManager.SESSION_KEYGUARD;
+
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Resources;
@@ -28,7 +30,10 @@
 import android.os.Trace;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
@@ -48,6 +53,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -156,6 +162,7 @@
     private final DozeParameters mDozeParameters;
     private final KeyguardStateController mKeyguardStateController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
+    private final SessionTracker mSessionTracker;
     private final Context mContext;
     private final int mWakeUpDelay;
     private int mMode;
@@ -273,7 +280,8 @@
             ScreenLifecycle screenLifecycle,
             AuthController authController,
             StatusBarStateController statusBarStateController,
-            KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
+            KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+            SessionTracker sessionTracker) {
         mContext = context;
         mPowerManager = powerManager;
         mShadeController = shadeController;
@@ -297,6 +305,7 @@
         mAuthController = authController;
         mStatusBarStateController = statusBarStateController;
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+        mSessionTracker = sessionTracker;
         dumpManager.registerDumpable(getClass().getName(), this);
     }
 
@@ -376,7 +385,7 @@
         mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
                 .setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
         Optional.ofNullable(BiometricUiEvent.SUCCESS_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
-                .ifPresent(UI_EVENT_LOGGER::log);
+                .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
 
         boolean unlockAllowed =
                 mKeyguardStateController.isOccluded()
@@ -641,7 +650,7 @@
         mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
                 .setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
         Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
-                .ifPresent(UI_EVENT_LOGGER::log);
+                .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
 
         if (biometricSourceType == BiometricSourceType.FINGERPRINT
                 && mUpdateMonitor.isUdfpsSupported()) {
@@ -656,7 +665,7 @@
 
             if (mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
                 startWakeAndUnlock(MODE_SHOW_BOUNCER);
-                UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+                UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
                 mNumConsecutiveFpFailures = 0;
             }
         }
@@ -670,7 +679,7 @@
                 .setType(MetricsEvent.TYPE_ERROR).setSubtype(toSubtype(biometricSourceType))
                 .addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
         Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
-                .ifPresent(UI_EVENT_LOGGER::log);
+                .ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
 
         // if we're on the shade and we're locked out, immediately show the bouncer
         if (biometricSourceType == BiometricSourceType.FINGERPRINT
@@ -680,7 +689,7 @@
                 && (mStatusBarStateController.getState() == StatusBarState.SHADE
                     || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
             startWakeAndUnlock(MODE_SHOW_BOUNCER);
-            UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+            UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
         }
         cleanup();
     }
@@ -786,6 +795,9 @@
         return mBiometricType;
     }
 
+    private @Nullable InstanceId getSessionId() {
+        return mSessionTracker.getSessionId(SESSION_KEYGUARD);
+    }
     /**
      * Translates biometric source type for logging purpose.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 5f222af..b4e07f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -72,7 +72,10 @@
         LOCKSCREEN_NOTIFICATION_FALSE_TOUCH(548),
 
         @UiEvent(doc = "Expand the notification panel while unlocked")
-        LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND(549);
+        LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND(549),
+
+        @UiEvent(doc = "Lockscreen > Tap on switch user icon")
+        LOCKSCREEN_SWITCH_USER_TAP(934);
 
         private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 34009f5..9d3f19a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -181,6 +181,7 @@
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -204,6 +205,7 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.wallet.controller.QuickAccessWalletController;
@@ -225,7 +227,8 @@
 import javax.inject.Provider;
 
 @StatusBarComponent.StatusBarScope
-public class NotificationPanelViewController extends PanelViewController {
+public class NotificationPanelViewController extends PanelViewController
+        implements NotifPanelEventSource {
 
     private static final boolean DEBUG = false;
 
@@ -667,6 +670,8 @@
 
     private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
 
+    private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
+
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -2443,9 +2448,15 @@
 
     private void updateQsExpansion() {
         if (mQs == null) return;
-        final float squishiness =
-                mQsExpandImmediate || mQsExpanded ? 1f : mNotificationStackScrollLayoutController
-                        .getNotificationSquishinessFraction();
+        final float squishiness;
+        if (mQsExpandImmediate || mQsExpanded) {
+            squishiness = 1;
+        } else if (mLockscreenShadeTransitionController.getQSDragProgress() > 0) {
+            squishiness = mLockscreenShadeTransitionController.getQSDragProgress();
+        } else {
+            squishiness = mNotificationStackScrollLayoutController
+                    .getNotificationSquishinessFraction();
+        }
         final float qsExpansionFraction = computeQsExpansionFraction();
         final float adjustedExpansionFraction = mShouldUseSplitNotificationShade
                 ? 1f : computeQsExpansionFraction();
@@ -2473,7 +2484,6 @@
         mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
         mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
         mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
-        mKeyguardStatusBarViewController.updateViewState();
 
         if (mCommunalViewController != null) {
             mCommunalViewController.updateQsExpansion(qsExpansionFraction);
@@ -3429,6 +3439,28 @@
         return mIsLaunchTransitionRunning;
     }
 
+    @Override
+    public void setIsLaunchAnimationRunning(boolean running) {
+        boolean wasRunning = isLaunchTransitionRunning();
+        super.setIsLaunchAnimationRunning(running);
+        if (wasRunning != isLaunchTransitionRunning()) {
+            for (Callbacks cb : mNotifEventSourceCallbacks) {
+                cb.onLaunchingActivityChanged(running);
+            }
+        }
+    }
+
+    @Override
+    protected void setIsClosing(boolean isClosing) {
+        boolean wasClosing = isClosing();
+        super.setIsClosing(isClosing);
+        if (wasClosing != isClosing) {
+            for (Callbacks cb : mNotifEventSourceCallbacks) {
+                cb.onPanelCollapsingChanged(isClosing);
+            }
+        }
+    }
+
     public void setLaunchTransitionEndRunnable(Runnable r) {
         mLaunchAnimationEndRunnable = r;
     }
@@ -4327,6 +4359,16 @@
                 .commitUpdate(mDisplayId);
     }
 
+    @Override
+    public void registerCallbacks(Callbacks callbacks) {
+        mNotifEventSourceCallbacks.addIfAbsent(callbacks);
+    }
+
+    @Override
+    public void unregisterCallbacks(Callbacks callbacks) {
+        mNotifEventSourceCallbacks.remove(callbacks);
+    }
+
     private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
         @Override
         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 54d0b03..dc6efba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -506,7 +506,7 @@
 
     private void endClosing() {
         if (mClosing) {
-            mClosing = false;
+            setIsClosing(false);
             onClosingFinished();
         }
     }
@@ -581,7 +581,7 @@
             boolean expandBecauseOfFalsing) {
         float target = expand ? getMaxPanelHeight() : 0;
         if (!expand) {
-            mClosing = true;
+            setIsClosing(true);
         }
         flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
     }
@@ -870,7 +870,7 @@
             notifyExpandingStarted();
 
             // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
-            mClosing = true;
+            setIsClosing(true);
             if (delayed) {
                 mNextCollapseSpeedUpFactor = speedUpFactor;
                 mView.postDelayed(mFlingCollapseRunnable, 120);
@@ -1151,6 +1151,14 @@
         mIsLaunchAnimationRunning = running;
     }
 
+    protected void setIsClosing(boolean isClosing) {
+        mClosing = isClosing;
+    }
+
+    protected boolean isClosing() {
+        return mClosing;
+    }
+
     public void collapseWithDuration(int animationDuration) {
         mFixedDuration = animationDuration;
         collapse(false /* delayed */, 1.0f /* speedUpFactor */);
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 80ae070..c09c3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1544,6 +1544,12 @@
     }
 
     private void inflateStatusBarWindow() {
+        if (mStatusBarComponent != null) {
+            // Tear down
+            for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
+                startable.stop();
+            }
+        }
         mStatusBarComponent = mStatusBarComponentFactory.create();
         mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
 
@@ -1572,6 +1578,11 @@
         mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
         // Connect in to the status bar manager service
         mCommandQueue.addCallback(mCommandQueueCallbacks);
+
+        // Perform all other initialization for StatusBarScope
+        for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
+            startable.start();
+        }
     }
 
     protected void startKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 61dba92..ad8e79e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -24,6 +24,7 @@
 import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.notification.collection.render.StatusBarNotifPanelEventSourceModule;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -35,6 +36,7 @@
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
+import java.util.Set;
 
 import javax.inject.Named;
 import javax.inject.Scope;
@@ -50,7 +52,10 @@
  * that it has many getter methods indicates that we need to access many of these classes from
  * outside the component. Should more items be moved *into* this component to avoid so many getters?
  */
-@Subcomponent(modules = {StatusBarViewModule.class})
+@Subcomponent(modules = {
+        StatusBarNotifPanelEventSourceModule.class,
+        StatusBarViewModule.class
+})
 @StatusBarComponent.StatusBarScope
 public interface StatusBarComponent {
     /**
@@ -70,8 +75,7 @@
     @interface StatusBarScope {}
 
     /**
-     * Creates a {@link NotificationShadeWindowView}/
-     * @return
+     * Creates a {@link NotificationShadeWindowView}.
      */
     @StatusBarScope
     NotificationShadeWindowView getNotificationShadeWindowView();
@@ -138,4 +142,18 @@
      */
     @StatusBarScope
     StatusBarInitializer getStatusBarInitializer();
+
+    /**
+     * Set of startables to be run after a StatusBarComponent has been constructed.
+     */
+    @StatusBarScope
+    Set<Startable> getStartables();
+
+    /**
+     * Performs initialization logic after {@link StatusBarComponent} has been constructed.
+     */
+    interface Startable {
+        void start();
+        void stop();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 33f2140..d903639 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -23,6 +23,7 @@
 import android.hardware.devicestate.DeviceStateManager;
 import android.util.Log;
 
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 76615af0..b591545 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -29,6 +29,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardVisibilityHelper;
 import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
@@ -49,6 +50,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -82,6 +84,7 @@
     private final KeyguardUserDetailAdapter mUserDetailAdapter;
     private final FeatureFlags mFeatureFlags;
     private final UserSwitchDialogController mUserSwitchDialogController;
+    private final UiEventLogger mUiEventLogger;
     private NotificationPanelViewController mNotificationPanelViewController;
     private UserAvatarView mUserAvatarView;
     UserSwitcherController.UserRecord mCurrentUser;
@@ -133,7 +136,8 @@
             Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
             ScreenOffAnimationController screenOffAnimationController,
             FeatureFlags featureFlags,
-            UserSwitchDialogController userSwitchDialogController) {
+            UserSwitchDialogController userSwitchDialogController,
+            UiEventLogger uiEventLogger) {
         super(view);
         if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
         mContext = context;
@@ -151,6 +155,7 @@
         mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
         mFeatureFlags = featureFlags;
         mUserSwitchDialogController = userSwitchDialogController;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -173,7 +178,10 @@
                 return;
             }
 
-            // Tapping anywhere in the view will open QS user panel
+            // Tapping anywhere in the view will open the user switcher
+            mUiEventLogger.log(
+                    LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
+
             if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
                 mUserSwitchDialogController.showDialog(mView);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 60938fb..c326835 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -21,6 +21,7 @@
 import android.os.UserManager;
 
 import com.android.internal.R;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
@@ -36,7 +37,6 @@
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
-import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index e59d2f23..d0fb91c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.GlobalRootComponent;
+import com.android.systemui.statusbar.tv.VpnStatusObserver;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
 import dagger.Binds;
@@ -34,4 +35,9 @@
     @IntoMap
     @ClassKey(TvNotificationHandler.class)
     CoreStartable bindTvNotificationHandler(TvNotificationHandler systemui);
+
+    @Binds
+    @IntoMap
+    @ClassKey(VpnStatusObserver.class)
+    CoreStartable bindVpnStatusObserver(VpnStatusObserver systemui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 11725ef..57c7f11 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -33,7 +33,6 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IVolumeController;
-import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.media.VolumePolicy;
@@ -47,7 +46,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
@@ -68,6 +66,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.util.RingerModeLiveData;
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.concurrency.ThreadFactory;
@@ -78,7 +77,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.inject.Inject;
@@ -135,7 +133,7 @@
     protected C mCallbacks = new C();
     private final State mState = new State();
     protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
-    private final Optional<Vibrator> mVibrator;
+    private final VibratorHelper mVibrator;
     private final boolean mHasVibrator;
     private boolean mShowA11yStream;
     private boolean mShowVolumeDialog;
@@ -173,7 +171,7 @@
             ThreadFactory theadFactory,
             AudioManager audioManager,
             NotificationManager notificationManager,
-            Optional<Vibrator> optionalVibrator,
+            VibratorHelper vibrator,
             IAudioService iAudioService,
             AccessibilityManager accessibilityManager,
             PackageManager packageManager,
@@ -199,8 +197,8 @@
         mBroadcastDispatcher = broadcastDispatcher;
         mObserver.init();
         mReceiver.init();
-        mVibrator = optionalVibrator;
-        mHasVibrator = mVibrator.isPresent() && mVibrator.get().hasVibrator();
+        mVibrator = vibrator;
+        mHasVibrator = mVibrator.hasVibrator();
         mAudioService = iAudioService;
 
         boolean accessibilityVolumeStreamActive = accessibilityManager
@@ -393,8 +391,7 @@
     }
 
     public void vibrate(VibrationEffect effect) {
-        mVibrator.ifPresent(
-                vibrator -> vibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES));
+        mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
     }
 
     public boolean hasVibrator() {
@@ -402,7 +399,7 @@
     }
 
     private void onNotifyVisibleW(boolean visible) {
-        if (mDestroyed) return; 
+        if (mDestroyed) return;
         mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
         if (!visible) {
             if (updateActiveStreamW(-1)) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index 6bc6505..aa671d1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -20,11 +20,12 @@
 import org.mockito.Mockito.verify
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.SessionTracker
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,9 +45,11 @@
     @Mock
     lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock
-    lateinit var dumpManager: DumpManager
-    @Mock
     lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker
+    @Mock
+    lateinit var sessionTracker: SessionTracker
+    @Mock
+    lateinit var sessionId: InstanceId
 
     @Captor
     lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -58,11 +61,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
+        whenever(sessionTracker.getSessionId(anyInt())).thenReturn(sessionId)
         keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
                 mContext,
                 uiEventLogger,
                 keyguardUpdateMonitor,
-                dumpManager)
+                sessionTracker)
     }
 
     @Test
@@ -76,7 +80,7 @@
 
         // THEN encrypted / lockdown state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+                .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN, sessionId)
     }
 
     @Test
@@ -93,7 +97,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT, sessionId)
     }
 
     @Test
@@ -110,7 +114,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE, sessionId)
     }
 
     @Test
@@ -128,9 +132,9 @@
 
         // THEN primary auth required state is logged with all the reasons
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT, sessionId)
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE, sessionId)
 
         // WHEN onStrongAuthStateChanged is called again
         updateMonitorCallback.onStrongAuthStateChanged(0)
@@ -152,7 +156,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT, sessionId)
 
         // WHEN face lockout is reset
         whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false)
@@ -160,7 +164,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET, sessionId)
     }
 
     @Test
@@ -176,7 +180,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT, sessionId)
 
         // WHEN fingerprint lockout is reset
         whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
@@ -184,7 +188,7 @@
 
         // THEN primary auth required state is logged
         verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
-                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET, sessionId)
     }
 
     fun captureUpdateMonitorCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 599e547..a819a7a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -120,6 +121,8 @@
     private FeatureFlags mFeatureFlags;
     @Mock
     private UserSwitcherController mUserSwitcherController;
+    @Mock
+    private SessionTracker mSessionTracker;
     private Configuration mConfiguration;
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -154,7 +157,8 @@
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
                 mConfigurationController, mFalsingCollector, mFalsingManager,
-                mUserSwitcherController, mFeatureFlags, mGlobalSettings).create(mSecurityCallback);
+                mUserSwitcherController, mFeatureFlags, mGlobalSettings,
+                mSessionTracker).create(mSecurityCallback);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index c6df1c1..72d72c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -45,6 +45,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.IdRes;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Insets;
@@ -63,9 +64,15 @@
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.decor.CornerDecorProvider;
+import com.android.systemui.decor.DecorProvider;
+import com.android.systemui.decor.OverlayWindow;
+import com.android.systemui.decor.PrivacyDotCornerDecorProviderImpl;
+import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.tuner.TunerService;
@@ -81,6 +88,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Collections;
 
 @RunWithLooper
@@ -96,6 +104,7 @@
     private SecureSettings mSecureSettings;
     private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private FakeThreadFactory mThreadFactory;
+    private ArrayList<DecorProvider> mDecorProviders;
     @Mock
     private TunerService mTunerService;
     @Mock
@@ -106,6 +115,16 @@
     private PrivacyDotViewController mDotViewController;
     @Mock
     private TypedArray mMockTypedArray;
+    @Mock
+    private PrivacyDotDecorProviderFactory mPrivacyDotDecorProviderFactory;
+    @Mock
+    private CornerDecorProvider mPrivacyDotTopLeftDecorProvider;
+    @Mock
+    private CornerDecorProvider mPrivacyDotTopRightDecorProvider;
+    @Mock
+    private CornerDecorProvider mPrivacyDotBottomLeftDecorProvider;
+    @Mock
+    private CornerDecorProvider mPrivacyDotBottomRightDecorProvider;
 
     @Before
     public void setup() {
@@ -129,9 +148,33 @@
         mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
         when(mMockTypedArray.length()).thenReturn(0);
 
+        mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
+                R.id.privacy_dot_top_left_container,
+                DisplayCutout.BOUNDS_POSITION_TOP,
+                DisplayCutout.BOUNDS_POSITION_LEFT,
+                R.layout.privacy_dot_top_left));
+
+        mPrivacyDotTopRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
+                R.id.privacy_dot_top_right_container,
+                DisplayCutout.BOUNDS_POSITION_TOP,
+                DisplayCutout.BOUNDS_POSITION_RIGHT,
+                R.layout.privacy_dot_top_right));
+
+        mPrivacyDotBottomLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
+                R.id.privacy_dot_bottom_left_container,
+                DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                DisplayCutout.BOUNDS_POSITION_LEFT,
+                R.layout.privacy_dot_bottom_left));
+
+        mPrivacyDotBottomRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
+                R.id.privacy_dot_bottom_right_container,
+                DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                DisplayCutout.BOUNDS_POSITION_RIGHT,
+                R.layout.privacy_dot_bottom_right));
+
         mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
                 mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
-                mThreadFactory) {
+                mThreadFactory, mPrivacyDotDecorProviderFactory) {
             @Override
             public void start() {
                 super.start();
@@ -157,7 +200,7 @@
     private void verifyRoundedCornerViewsVisibility(
             @DisplayCutout.BoundsPosition final int overlayId,
             @View.Visibility final int visibility) {
-        final View overlay = mScreenDecorations.mOverlays[overlayId];
+        final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView();
         final View left = overlay.findViewById(R.id.left);
         final View right = overlay.findViewById(R.id.right);
         assertNotNull(left);
@@ -166,23 +209,42 @@
         assertThat(right.getVisibility()).isEqualTo(visibility);
     }
 
+    @Nullable
+    private View findViewFromOverlays(@IdRes int id) {
+        for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
+            if (overlay == null) {
+                continue;
+            }
+
+            View view = overlay.getRootView().findViewById(id);
+            if (view != null) {
+                return view;
+            }
+        }
+        return null;
+    }
+
     private void verifyTopDotViewsNullable(final boolean isAssertNull) {
+        View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
+        View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
         if (isAssertNull) {
-            assertNull(mScreenDecorations.mTopLeftDot);
-            assertNull(mScreenDecorations.mTopRightDot);
+            assertNull(tl);
+            assertNull(tr);
         } else {
-            assertNotNull(mScreenDecorations.mTopLeftDot);
-            assertNotNull(mScreenDecorations.mTopRightDot);
+            assertNotNull(tl);
+            assertNotNull(tr);
         }
     }
 
     private void verifyBottomDotViewsNullable(final boolean isAssertNull) {
+        View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
+        View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
         if (isAssertNull) {
-            assertNull(mScreenDecorations.mBottomLeftDot);
-            assertNull(mScreenDecorations.mBottomRightDot);
+            assertNull(bl);
+            assertNull(br);
         } else {
-            assertNotNull(mScreenDecorations.mBottomLeftDot);
-            assertNotNull(mScreenDecorations.mBottomRightDot);
+            assertNotNull(bl);
+            assertNotNull(br);
         }
     }
 
@@ -193,14 +255,18 @@
 
     private void verifyTopDotViewsVisibility(@View.Visibility final int visibility) {
         verifyTopDotViewsNullable(false);
-        assertThat(mScreenDecorations.mTopLeftDot.getVisibility()).isEqualTo(visibility);
-        assertThat(mScreenDecorations.mTopRightDot.getVisibility()).isEqualTo(visibility);
+        View tl = findViewFromOverlays(R.id.privacy_dot_top_left_container);
+        View tr = findViewFromOverlays(R.id.privacy_dot_top_right_container);
+        assertThat(tl.getVisibility()).isEqualTo(visibility);
+        assertThat(tr.getVisibility()).isEqualTo(visibility);
     }
 
     private void verifyBottomDotViewsVisibility(@View.Visibility final int visibility) {
         verifyBottomDotViewsNullable(false);
-        assertThat(mScreenDecorations.mBottomLeftDot.getVisibility()).isEqualTo(visibility);
-        assertThat(mScreenDecorations.mBottomRightDot.getVisibility()).isEqualTo(visibility);
+        View bl = findViewFromOverlays(R.id.privacy_dot_bottom_left_container);
+        View br = findViewFromOverlays(R.id.privacy_dot_bottom_right_container);
+        assertThat(bl.getVisibility()).isEqualTo(visibility);
+        assertThat(br.getVisibility()).isEqualTo(visibility);
     }
 
     private void verifyDotViewsVisibility(@View.Visibility final int visibility) {
@@ -219,32 +285,32 @@
 
         if (left) {
             assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
-            verify(mWindowManager, times(1))
-                    .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
+            verify(mWindowManager, times(1)).addView(
+                    eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()), any());
         } else {
             assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
         }
 
         if (top) {
             assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
-            verify(mWindowManager, times(1))
-                    .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
+            verify(mWindowManager, times(1)).addView(
+                    eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()), any());
         } else {
             assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
         }
 
         if (right) {
             assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
-            verify(mWindowManager, times(1))
-                    .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
+            verify(mWindowManager, times(1)).addView(
+                    eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()), any());
         } else {
             assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
         }
 
         if (bottom) {
             assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
-            verify(mWindowManager, times(1))
-                    .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
+            verify(mWindowManager, times(1)).addView(
+                    eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()), any());
         } else {
             assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
         }
@@ -381,18 +447,18 @@
         doReturn(null).when(mScreenDecorations).getCutout();
 
         mScreenDecorations.start();
-        View leftRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.left);
-        View rightRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].findViewById(R.id.right);
+        View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
+                .findViewById(R.id.left);
+        View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
+                .findViewById(R.id.right);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(leftRoundedCorner, new Point(testTopRadius, testTopRadius));
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(rightRoundedCorner, new Point(testTopRadius, testTopRadius));
-        leftRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.left);
-        rightRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].findViewById(R.id.right);
+        leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
+                .findViewById(R.id.left);
+        rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
+                .findViewById(R.id.right);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(leftRoundedCorner, new Point(testBottomRadius, testBottomRadius));
         verify(mScreenDecorations, atLeastOnce())
@@ -414,26 +480,26 @@
         mScreenDecorations.start();
         final Point topRadius = new Point(testTopRadius, testTopRadius);
         final Point bottomRadius = new Point(testBottomRadius, testBottomRadius);
-        View leftRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.left);
+        View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+                .findViewById(R.id.left);
         boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
 
-        View rightRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].findViewById(R.id.right);
+        View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+                .findViewById(R.id.right);
         isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
 
-        leftRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.left);
+        leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+                .findViewById(R.id.left);
         isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
 
-        rightRoundedCorner =
-                mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].findViewById(R.id.right);
+        rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+                .findViewById(R.id.right);
         isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right);
         verify(mScreenDecorations, atLeastOnce())
                 .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
@@ -790,6 +856,22 @@
 
         mScreenDecorations.onConfigurationChanged(new Configuration());
         verifyOverlaysExistAndAdded(true, false, true, false);
+
+        // Verify each privacy dot id appears only once
+        mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
+            int findCount = 0;
+            for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
+                if (overlay == null) {
+                    continue;
+                }
+                final View view = overlay.getRootView().findViewById(viewId);
+                if (view != null) {
+                    findCount++;
+                }
+            }
+            assertEquals(1, findCount);
+        });
+
     }
 
     @Test
@@ -985,8 +1067,16 @@
                 R.bool.config_roundedCornerMultipleRadius, multipleRadius);
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
-        mContext.getOrCreateTestableResources().addOverride(
-                R.bool.config_enablePrivacyDot, privacyDot);
+
+        mDecorProviders = new ArrayList<>();
+        if (privacyDot) {
+            mDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
+            mDecorProviders.add(mPrivacyDotTopRightDecorProvider);
+            mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
+            mDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
+        }
+        when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders);
+        when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot);
     }
 
     private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 159bdba..35e838b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -44,7 +44,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.VibrationAttributes;
-import android.os.Vibrator;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.LayoutInflater;
@@ -64,6 +63,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -141,7 +141,7 @@
     @Mock
     private ScreenLifecycle mScreenLifecycle;
     @Mock
-    private Vibrator mVibrator;
+    private VibratorHelper mVibrator;
     @Mock
     private UdfpsHapticsSimulator mUdfpsHapticsSimulator;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 55509d1..9908d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -20,13 +20,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsMetricsLogger
-import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.wm.shell.TaskViewFactory
-import dagger.Lazy
-import java.util.Optional
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,15 +39,14 @@
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.util.Optional
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ControlActionCoordinatorImplTest : SysuiTestCase() {
 
     @Mock
-    private lateinit var uiController: ControlsUiController
-    @Mock
-    private lateinit var lazyUiController: Lazy<ControlsUiController>
+    private lateinit var vibratorHelper: VibratorHelper
     @Mock
     private lateinit var keyguardStateController: KeyguardStateController
     @Mock
@@ -59,8 +56,6 @@
     @Mock
     private lateinit var activityStarter: ActivityStarter
     @Mock
-    private lateinit var globalActionsComponent: GlobalActionsComponent
-    @Mock
     private lateinit var taskViewFactory: Optional<TaskViewFactory>
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private lateinit var cvh: ControlViewHolder
@@ -86,11 +81,9 @@
             uiExecutor,
             activityStarter,
             keyguardStateController,
-            globalActionsComponent,
             taskViewFactory,
-            getFakeBroadcastDispatcher(),
-            lazyUiController,
-            metricsLogger
+            metricsLogger,
+            vibratorHelper
         ))
 
         `when`(cvh.cws.ci.controlId).thenReturn(ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
new file mode 100644
index 0000000..ca74df0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.decor
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.DisplayCutout
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class OverlayWindowTest : SysuiTestCase() {
+
+    companion object {
+        private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container
+        private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right
+    }
+
+    private lateinit var overlay: OverlayWindow
+
+    @Mock private lateinit var layoutInflater: LayoutInflater
+    @Mock private lateinit var decorProvider: DecorProvider
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        layoutInflater = spy(LayoutInflater.from(mContext))
+
+        overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
+
+        whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID)
+        whenever(decorProvider.inflateView(
+            eq(layoutInflater),
+            eq(overlay.rootView),
+            anyInt())
+        ).then {
+            val layoutInflater = it.getArgument<LayoutInflater>(0)
+            val parent = it.getArgument<ViewGroup>(1)
+            layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent)
+            return@then parent.getChildAt(parent.childCount - 1)
+        }
+    }
+
+    @Test
+    fun testAnyBoundsPositionShallNoExceptionForConstructor() {
+        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT)
+        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP)
+        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
+        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM)
+    }
+
+    @Test
+    fun testAddProvider() {
+        @Surface.Rotation val rotation = Surface.ROTATION_270
+        overlay.addDecorProvider(decorProvider, rotation)
+        verify(decorProvider, Mockito.times(1)).inflateView(
+                eq(layoutInflater), eq(overlay.rootView), eq(rotation))
+        val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
+        Assert.assertNotNull(viewFoundFromRootView)
+        Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID))
+    }
+
+    @Test
+    fun testRemoveView() {
+        @Surface.Rotation val rotation = Surface.ROTATION_270
+        overlay.addDecorProvider(decorProvider, rotation)
+        overlay.removeView(TEST_DECOR_VIEW_ID)
+        val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
+        Assert.assertNull(viewFoundFromRootView)
+        Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
new file mode 100644
index 0000000..bac0817
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.decor
+
+import android.content.res.Resources
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.DisplayCutout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
+    private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
+
+    @Mock private lateinit var resources: Resources
+
+    @Before
+    fun setUp() {
+        resources = spy(mContext.resources)
+        mPrivacyDotDecorProviderFactory = PrivacyDotDecorProviderFactory(resources)
+    }
+
+    private fun setPrivacyDotResources(isEnable: Boolean) {
+        whenever(resources.getBoolean(R.bool.config_enablePrivacyDot)).thenReturn(isEnable)
+    }
+
+    @Test
+    fun testGetNoCornerDecorProviderWithNoPrivacyDot() {
+        setPrivacyDotResources(false)
+
+        Assert.assertEquals(false, mPrivacyDotDecorProviderFactory.hasProviders)
+        Assert.assertEquals(0, mPrivacyDotDecorProviderFactory.providers.size)
+    }
+
+    @Test
+    fun testGet4CornerDecorProvidersWithPrivacyDot() {
+        setPrivacyDotResources(true)
+        val providers = mPrivacyDotDecorProviderFactory.providers
+
+        Assert.assertEquals(true, mPrivacyDotDecorProviderFactory.hasProviders)
+        Assert.assertEquals(4, providers.size)
+        Assert.assertEquals(1, providers.count {
+            ((it.viewId == R.id.privacy_dot_top_left_container)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+        })
+        Assert.assertEquals(1, providers.count {
+            ((it.viewId == R.id.privacy_dot_top_right_container)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+        })
+        Assert.assertEquals(1, providers.count {
+            ((it.viewId == R.id.privacy_dot_bottom_left_container)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+        })
+        Assert.assertEquals(1, providers.count {
+            ((it.viewId == R.id.privacy_dot_bottom_right_container)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                    and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+        })
+    }
+}
\ No newline at end of file
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 6b156a4..b3b5fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -33,12 +33,12 @@
 
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -65,10 +65,6 @@
     @Rule
     public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
 
-    @Rule
-    public SysuiTestableContext mContext = new SysuiTestableContext(
-            InstrumentationRegistry.getContext(), mLeakCheck);
-
     WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
 
     @Mock
@@ -89,6 +85,13 @@
     @Mock
     DreamOverlayContainerViewController mDreamOverlayContainerViewController;
 
+    @Mock
+    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    @Mock
+    DreamOverlayTouchMonitor mDreamOverlayTouchMonitor;
+
+
     DreamOverlayService mService;
 
     @Before
@@ -102,6 +105,8 @@
                 .thenReturn(mLifecycleOwner);
         when(mDreamOverlayComponent.getLifecycleRegistry())
                 .thenReturn(mLifecycleRegistry);
+        when(mDreamOverlayComponent.getDreamOverlayTouchMonitor())
+                .thenReturn(mDreamOverlayTouchMonitor);
         when(mDreamOverlayComponentFactory
                 .create(any(), any()))
                 .thenReturn(mDreamOverlayComponent);
@@ -109,7 +114,8 @@
                 .thenReturn(mDreamOverlayContainerView);
 
         mService = new DreamOverlayService(mContext, mMainExecutor,
-                mDreamOverlayComponentFactory);
+                mDreamOverlayComponentFactory,
+                mKeyguardUpdateMonitor);
         final IBinder proxy = mService.onBind(new Intent());
         final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
 
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
index afc0309d..feeea5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationCollectionLiveDataTest.java
@@ -85,7 +85,9 @@
 
         verify(observer).onChanged(collectionCaptor.capture());
 
-        assertThat(collectionCaptor.getValue().equals(targetCollection)).isTrue();
+        final Collection collection =  collectionCaptor.getValue();
+        assertThat(collection.containsAll(targetCollection)
+                && targetCollection.containsAll(collection)).isTrue();
         Mockito.clearInvocations(observer);
     }
 }
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
index d080bbc..967b30d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -81,7 +81,7 @@
                 final boolean properDirection = (invalidPosition & position) != invalidPosition;
 
                 try {
-                    final ComplicationLayoutParams params = new ComplicationLayoutParams(
+                    new ComplicationLayoutParams(
                             100,
                             100,
                             position,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
new file mode 100644
index 0000000..74b217b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.touch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.util.Pair;
+import android.view.GestureDetector;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.touch.dagger.InputSessionComponent;
+import com.android.systemui.shared.system.InputChannelCompat;
+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;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private static class Environment {
+        private final InputSessionComponent.Factory mInputFactory;
+        private final InputSession mInputSession;
+        private final Lifecycle mLifecycle;
+        private final LifecycleOwner mLifecycleOwner;
+        private final DreamOverlayTouchMonitor mMonitor;
+        private final DefaultLifecycleObserver mLifecycleObserver;
+        private final InputChannelCompat.InputEventListener mEventListener;
+        private final GestureDetector.OnGestureListener mGestureListener;
+        private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
+        Environment(Set<DreamTouchHandler> handlers) {
+            mLifecycle = Mockito.mock(Lifecycle.class);
+            mLifecycleOwner = Mockito.mock(LifecycleOwner.class);
+
+            mInputFactory = Mockito.mock(InputSessionComponent.Factory.class);
+            final InputSessionComponent inputComponent = Mockito.mock(InputSessionComponent.class);
+            mInputSession = Mockito.mock(InputSession.class);
+
+            when(mInputFactory.create(any(), any(), any(), anyBoolean()))
+                    .thenReturn(inputComponent);
+            when(inputComponent.getInputSession()).thenReturn(mInputSession);
+
+            mMonitor = new DreamOverlayTouchMonitor(mExecutor, mLifecycle, mInputFactory, handlers);
+            mMonitor.init();
+
+            final ArgumentCaptor<LifecycleObserver> lifecycleObserverCaptor =
+                    ArgumentCaptor.forClass(LifecycleObserver.class);
+            verify(mLifecycle).addObserver(lifecycleObserverCaptor.capture());
+            assertThat(lifecycleObserverCaptor.getValue() instanceof DefaultLifecycleObserver)
+                    .isTrue();
+            mLifecycleObserver = (DefaultLifecycleObserver) lifecycleObserverCaptor.getValue();
+
+            updateLifecycle(observer -> observer.first.onResume(observer.second));
+
+            // Capture creation request.
+            final ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor =
+                    ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+            final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+                    ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+            verify(mInputFactory).create(any(), inputEventListenerCaptor.capture(),
+                    gestureListenerCaptor.capture(),
+                    eq(true));
+            mEventListener = inputEventListenerCaptor.getValue();
+            mGestureListener = gestureListenerCaptor.getValue();
+        }
+
+        void executeAll() {
+            mExecutor.runAllReady();
+        }
+
+        void publishInputEvent(InputEvent event) {
+            mEventListener.onInputEvent(event);
+        }
+
+        void publishGestureEvent(Consumer<GestureDetector.OnGestureListener> listenerConsumer) {
+            listenerConsumer.accept(mGestureListener);
+        }
+
+        void updateLifecycle(Consumer<Pair<DefaultLifecycleObserver, LifecycleOwner>> consumer) {
+            consumer.accept(Pair.create(mLifecycleObserver, mLifecycleOwner));
+        }
+
+        void verifyInputSessionDispose() {
+            verify(mInputSession).dispose();
+            Mockito.clearInvocations(mInputSession);
+        }
+    }
+
+    @Test
+    public void testInputEventPropagation() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final InputChannelCompat.InputEventListener eventListener =
+                registerInputEventListener(touchHandler);
+
+        // First event will be missed since we register after the execution loop,
+        final InputEvent event = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(event);
+        verify(eventListener).onInputEvent(eq(event));
+    }
+
+    @Test
+    public void testInputGesturePropagation() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final GestureDetector.OnGestureListener gestureListener =
+                registerGestureListener(touchHandler);
+
+        final MotionEvent event = Mockito.mock(MotionEvent.class);
+        environment.publishGestureEvent(onGestureListener -> onGestureListener.onShowPress(event));
+        verify(gestureListener).onShowPress(eq(event));
+    }
+
+    @Test
+    public void testGestureConsumption() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final GestureDetector.OnGestureListener gestureListener =
+                registerGestureListener(touchHandler);
+
+        when(gestureListener.onDown(any())).thenReturn(true);
+        final MotionEvent event = Mockito.mock(MotionEvent.class);
+        environment.publishGestureEvent(onGestureListener -> {
+            assertThat(onGestureListener.onDown(event)).isTrue();
+        });
+
+        verify(gestureListener).onDown(eq(event));
+    }
+
+    @Test
+    public void testBroadcast() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+        final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler, touchHandler2)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        final HashSet<InputChannelCompat.InputEventListener> inputListeners = new HashSet<>();
+
+        inputListeners.add(registerInputEventListener(touchHandler));
+        inputListeners.add(registerInputEventListener(touchHandler));
+        inputListeners.add(registerInputEventListener(touchHandler2));
+
+        final MotionEvent event = Mockito.mock(MotionEvent.class);
+        environment.publishInputEvent(event);
+
+        inputListeners
+                .stream()
+                .forEach(inputEventListener -> verify(inputEventListener).onInputEvent(event));
+    }
+
+    @Test
+    public void testPush() throws InterruptedException, ExecutionException {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+        final InputChannelCompat.InputEventListener eventListener =
+                registerInputEventListener(session);
+
+        final ListenableFuture<DreamTouchHandler.TouchSession> frontSessionFuture = session.push();
+        environment.executeAll();
+        final DreamTouchHandler.TouchSession frontSession = frontSessionFuture.get();
+        final InputChannelCompat.InputEventListener frontEventListener =
+                registerInputEventListener(frontSession);
+
+        final MotionEvent event = Mockito.mock(MotionEvent.class);
+        environment.publishInputEvent(event);
+
+        verify(frontEventListener).onInputEvent(eq(event));
+        verify(eventListener, never()).onInputEvent(any());
+
+        Mockito.clearInvocations(eventListener, frontEventListener);
+
+        ListenableFuture<DreamTouchHandler.TouchSession> sessionFuture = frontSession.pop();
+        environment.executeAll();
+
+        DreamTouchHandler.TouchSession returnedSession = sessionFuture.get();
+        assertThat(session == returnedSession).isTrue();
+
+        environment.executeAll();
+
+        final MotionEvent followupEvent = Mockito.mock(MotionEvent.class);
+        environment.publishInputEvent(followupEvent);
+
+        verify(eventListener).onInputEvent(eq(followupEvent));
+        verify(frontEventListener, never()).onInputEvent(any());
+    }
+
+    @Test
+    public void testPause() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final InputChannelCompat.InputEventListener eventListener =
+                registerInputEventListener(touchHandler);
+
+        // First event will be missed since we register after the execution loop,
+        final InputEvent event = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(event);
+        verify(eventListener).onInputEvent(eq(event));
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onPause(observerOwnerPair.second);
+        });
+
+        environment.verifyInputSessionDispose();
+    }
+
+    @Test
+    public void testPilfering() {
+        final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
+        final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler1, touchHandler2)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        final DreamTouchHandler.TouchSession session1 = captureSession(touchHandler1);
+        final GestureDetector.OnGestureListener gestureListener1 =
+                registerGestureListener(session1);
+
+        final DreamTouchHandler.TouchSession session2 = captureSession(touchHandler2);
+        final GestureDetector.OnGestureListener gestureListener2 =
+                registerGestureListener(session2);
+        when(gestureListener2.onDown(any())).thenReturn(true);
+
+        final MotionEvent gestureEvent = Mockito.mock(MotionEvent.class);
+        environment.publishGestureEvent(
+                onGestureListener -> onGestureListener.onDown(gestureEvent));
+
+        Mockito.clearInvocations(gestureListener1, gestureListener2);
+
+        final MotionEvent followupEvent = Mockito.mock(MotionEvent.class);
+        environment.publishGestureEvent(
+                onGestureListener -> onGestureListener.onDown(followupEvent));
+
+        verify(gestureListener1, never()).onDown(any());
+        verify(gestureListener2).onDown(eq(followupEvent));
+    }
+
+    public GestureDetector.OnGestureListener registerGestureListener(DreamTouchHandler handler) {
+        final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
+                GestureDetector.OnGestureListener.class);
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+        verify(handler).onSessionStart(sessionCaptor.capture());
+        sessionCaptor.getValue().registerGestureListener(gestureListener);
+
+        return gestureListener;
+    }
+
+    public GestureDetector.OnGestureListener registerGestureListener(
+            DreamTouchHandler.TouchSession session) {
+        final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
+                GestureDetector.OnGestureListener.class);
+        session.registerGestureListener(gestureListener);
+
+        return gestureListener;
+    }
+
+    public InputChannelCompat.InputEventListener registerInputEventListener(
+            DreamTouchHandler.TouchSession session) {
+        final InputChannelCompat.InputEventListener eventListener = Mockito.mock(
+                InputChannelCompat.InputEventListener.class);
+        session.registerInputListener(eventListener);
+
+        return eventListener;
+    }
+
+    public DreamTouchHandler.TouchSession captureSession(DreamTouchHandler handler) {
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> sessionCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+        verify(handler).onSessionStart(sessionCaptor.capture());
+        return sessionCaptor.getValue();
+    }
+
+    public InputChannelCompat.InputEventListener registerInputEventListener(
+            DreamTouchHandler handler) {
+        return registerInputEventListener(captureSession(handler));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
new file mode 100644
index 0000000..2cb1939
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.dump
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogcatEchoTracker
+
+/**
+ * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
+ */
+fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
+    LogBuffer(name, 50, 50, LogcatEchoTrackerAlways())
+
+/**
+ * A [LogcatEchoTracker] that always allows echoing to the logcat.
+ */
+class LogcatEchoTrackerAlways : LogcatEchoTracker {
+    override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = true
+    override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index e3a7e3b..71fc8ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -61,6 +61,7 @@
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -115,6 +116,7 @@
     @Mock private PackageManager mPackageManager;
     @Mock private Handler mHandler;
     @Mock private UserContextProvider mUserContextProvider;
+    @Mock private VibratorHelper mVibratorHelper;
     @Mock private StatusBar mStatusBar;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
@@ -143,7 +145,7 @@
                 mTelephonyListenerManager,
                 mGlobalSettings,
                 mSecureSettings,
-                null,
+                mVibratorHelper,
                 mResources,
                 mConfigurationController,
                 mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d7c00fb..5ed1d65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -38,7 +38,6 @@
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Vibrator;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
@@ -62,6 +61,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -104,7 +104,7 @@
     private @Mock DumpManager mDumpManager;
     private @Mock AccessibilityManager mAccessibilityManager;
     private @Mock ConfigurationController mConfigurationController;
-    private @Mock Vibrator mVibrator;
+    private @Mock VibratorHelper mVibrator;
     private @Mock AuthRippleController mAuthRippleController;
     private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
new file mode 100644
index 0000000..ad908e7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.statusbar
+
+import android.media.AudioAttributes
+import android.os.UserHandle
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class VibratorHelperTest : SysuiTestCase() {
+
+    @JvmField @Rule
+    var rule = MockitoJUnit.rule()
+
+    @Mock lateinit var vibrator: Vibrator
+    @Mock lateinit var executor: Executor
+    @Captor lateinit var backgroundTaskCaptor: ArgumentCaptor<Runnable>
+    lateinit var vibratorHelper: VibratorHelper
+
+    @Before
+    fun setup() {
+        vibratorHelper = VibratorHelper(vibrator, executor)
+        whenever(vibrator.hasVibrator()).thenReturn(true)
+    }
+
+    @Test
+    fun testVibrate() {
+        vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
+        verifyAsync().vibrate(any(VibrationEffect::class.java),
+                any(VibrationAttributes::class.java))
+    }
+
+    @Test
+    fun testVibrate2() {
+        vibratorHelper.vibrate(UserHandle.USER_CURRENT, "package",
+                mock(VibrationEffect::class.java), "reason",
+                mock(VibrationAttributes::class.java))
+        verifyAsync().vibrate(eq(UserHandle.USER_CURRENT), eq("package"),
+                any(VibrationEffect::class.java), eq("reason"),
+                any(VibrationAttributes::class.java))
+    }
+
+    @Test
+    fun testVibrate3() {
+        vibratorHelper.vibrate(mock(VibrationEffect::class.java), mock(AudioAttributes::class.java))
+        verifyAsync().vibrate(any(VibrationEffect::class.java), any(AudioAttributes::class.java))
+    }
+
+    @Test
+    fun testVibrate4() {
+        vibratorHelper.vibrate(mock(VibrationEffect::class.java))
+        verifyAsync().vibrate(any(VibrationEffect::class.java))
+    }
+
+    @Test
+    fun testHasVibrator() {
+        assertThat(vibratorHelper.hasVibrator()).isTrue()
+        verify(vibrator).hasVibrator()
+    }
+
+    @Test
+    fun testCancel() {
+        vibratorHelper.cancel()
+        verifyAsync().cancel()
+    }
+
+    private fun verifyAsync(): Vibrator {
+        verify(executor).execute(backgroundTaskCaptor.capture())
+        verify(vibrator).hasVibrator()
+        backgroundTaskCaptor.value.run()
+
+        return verify(vibrator)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 25dd23a..8fb066b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -112,7 +112,7 @@
     private CollectionReadyForBuildListener mReadyForBuildListener;
     private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
     private List<NotificationEntry> mEntrySet = new ArrayList<>();
-    private List<ListEntry> mBuiltList;
+    private List<ListEntry> mBuiltList = new ArrayList<>();
     private TestableStabilityManager mStabilityManager;
     private TestableNotifFilter mFinalizeFilter;
 
@@ -368,6 +368,50 @@
     }
 
     @Test
+    public void testGroupsWhoLoseAllChildrenToPromotionSuppressSummary() {
+        // GIVEN a group with two children
+        addGroupChild(0, PACKAGE_2, GROUP_1);
+        addGroupSummary(1, PACKAGE_2, GROUP_1);
+        addGroupChild(2, PACKAGE_2, GROUP_1);
+
+        // GIVEN a promoter that will promote one of children to top level
+        mListBuilder.addPromoter(new IdPromoter(0, 2));
+
+        // WHEN we build the list
+        dispatchBuild();
+
+        // THEN both children end up at top level (because group is now too small)
+        verifyBuiltList(
+                notif(0),
+                notif(2)
+        );
+
+        // THEN the summary is discarded
+        assertNull(mEntrySet.get(1).getParent());
+    }
+
+    @Test
+    public void testGroupsWhoLoseOnlyChildToPromotionSuppressSummary() {
+        // GIVEN a group with two children
+        addGroupChild(0, PACKAGE_2, GROUP_1);
+        addGroupSummary(1, PACKAGE_2, GROUP_1);
+
+        // GIVEN a promoter that will promote one of children to top level
+        mListBuilder.addPromoter(new IdPromoter(0));
+
+        // WHEN we build the list
+        dispatchBuild();
+
+        // THEN both children end up at top level (because group is now too small)
+        verifyBuiltList(
+                notif(0)
+        );
+
+        // THEN the summary is discarded
+        assertNull(mEntrySet.get(1).getParent());
+    }
+
+    @Test
     public void testPreviousParentsAreSetProperly() {
         // GIVEN a notification that is initially added to the list
         PackageFilter filter = new PackageFilter(PACKAGE_2);
@@ -1642,6 +1686,19 @@
     }
 
     @Test
+    public void testPipelineRunDisallowedDueToVisualStability() {
+        // GIVEN pipeline run not allowed due to visual stability
+        mStabilityManager.setAllowPipelineRun(false);
+
+        // WHEN we try to run the pipeline with a change
+        addNotif(0, PACKAGE_1);
+        dispatchBuild();
+
+        // THEN there is no change; the pipeline did not run
+        verifyBuiltList();
+    }
+
+    @Test
     public void testIsSorted() {
         Comparator<Integer> intCmp = Integer::compare;
         assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
@@ -2037,6 +2094,7 @@
     }
 
     private static class TestableStabilityManager extends NotifStabilityManager {
+        boolean mAllowPipelineRun = true;
         boolean mAllowGroupChanges = true;
         boolean mAllowSectionChanges = true;
         boolean mAllowEntryReodering = true;
@@ -2060,6 +2118,15 @@
             return this;
         }
 
+        TestableStabilityManager setAllowPipelineRun(boolean allowPipelineRun) {
+            mAllowPipelineRun = allowPipelineRun;
+            return this;
+        }
+
+        @Override
+        public boolean isPipelineRunAllowed() {
+            return mAllowPipelineRun;
+        }
 
         @Override
         public void onBeginRun() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index c67a233..144eefb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -15,14 +15,20 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
+import android.app.Notification.GROUP_ALERT_ALL
+import android.app.Notification.GROUP_ALERT_SUMMARY
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -32,6 +38,7 @@
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -64,10 +71,13 @@
     private lateinit var mCollectionListener: NotifCollectionListener
     private lateinit var mNotifPromoter: NotifPromoter
     private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
+    private lateinit var mBeforeTransformGroupsListener: OnBeforeTransformGroupsListener
+    private lateinit var mBeforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
     private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
     private lateinit var mNotifSectioner: NotifSectioner
 
     private val mNotifPipeline: NotifPipeline = mock()
+    private val mLogger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
     private val mHeadsUpManager: HeadsUpManager = mock()
     private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
     private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
@@ -76,12 +86,24 @@
     private val mHeaderController: NodeController = mock()
 
     private lateinit var mEntry: NotificationEntry
-    private val mExecutor = FakeExecutor(FakeSystemClock())
+    private lateinit var mGroupSummary: NotificationEntry
+    private lateinit var mGroupPriority: NotificationEntry
+    private lateinit var mGroupSibling1: NotificationEntry
+    private lateinit var mGroupSibling2: NotificationEntry
+    private lateinit var mGroupChild1: NotificationEntry
+    private lateinit var mGroupChild2: NotificationEntry
+    private lateinit var mGroupChild3: NotificationEntry
+    private val mSystemClock = FakeSystemClock()
+    private val mExecutor = FakeExecutor(mSystemClock)
     private val mHuns: ArrayList<NotificationEntry> = ArrayList()
+    private lateinit var mHelper: NotificationGroupTestHelper
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        mHelper = NotificationGroupTestHelper(mContext)
         mCoordinator = HeadsUpCoordinator(
+            mLogger,
+            mSystemClock,
             mHeadsUpManager,
             mHeadsUpViewBinder,
             mNotificationInterruptStateProvider,
@@ -100,6 +122,12 @@
         mNotifLifetimeExtender = withArgCaptor {
             verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
         }
+        mBeforeTransformGroupsListener = withArgCaptor {
+            verify(mNotifPipeline).addOnBeforeTransformGroupsListener(capture())
+        }
+        mBeforeFinalizeFilterListener = withArgCaptor {
+            verify(mNotifPipeline).addOnBeforeFinalizeFilterListener(capture())
+        }
         mOnHeadsUpChangedListener = withArgCaptor {
             verify(mHeadsUpManager).addListener(capture())
         }
@@ -116,6 +144,16 @@
         mNotifSectioner = mCoordinator.sectioner
         mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
         mEntry = NotificationEntryBuilder().build()
+        // Same summary we can use for either set of children
+        mGroupSummary = mHelper.createSummaryNotification(GROUP_ALERT_ALL, 0, "summary", 500)
+        // One set of children with GROUP_ALERT_SUMMARY
+        mGroupPriority = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 0, "priority", 400)
+        mGroupSibling1 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 1, "sibling", 300)
+        mGroupSibling2 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 2, "sibling", 200)
+        // Another set of children with GROUP_ALERT_ALL
+        mGroupChild1 = mHelper.createChildNotification(GROUP_ALERT_ALL, 1, "child", 350)
+        mGroupChild2 = mHelper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250)
+        mGroupChild3 = mHelper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150)
     }
 
     @Test
@@ -156,6 +194,34 @@
     }
 
     @Test
+    fun testPromotesAddedHUN() {
+        // GIVEN the current entry should heads up
+        whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+        // WHEN the notification is added but not yet binding
+        mCollectionListener.onEntryAdded(mEntry)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
+
+        // THEN only promote mEntry
+        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+    }
+
+    @Test
+    fun testPromotesBindingHUN() {
+        // GIVEN the current entry should heads up
+        whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+        // WHEN the notification started binding on the previous run
+        mCollectionListener.onEntryAdded(mEntry)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+        verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), any())
+
+        // THEN only promote mEntry
+        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+    }
+
+    @Test
     fun testPromotesCurrentHUN() {
         // GIVEN the current HUN is set to mEntry
         addHUN(mEntry)
@@ -198,6 +264,9 @@
         whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
 
         mCollectionListener.onEntryAdded(mEntry)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+        verify(mHeadsUpManager, never()).showNotification(mEntry)
         withArgCaptor<BindCallback> {
             verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
         }.onBindFinished(mEntry)
@@ -211,6 +280,8 @@
         // WHEN a notification shouldn't HUN and its inflation is finished
         whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
         mCollectionListener.onEntryAdded(mEntry)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
 
         // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
         verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
@@ -235,4 +306,296 @@
         whenever(mHeadsUpManager.topEntry).thenReturn(entry)
         mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun testTransferIsolatedChildAlert_withGroupAlertSummary() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupSibling1))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupSibling1)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupSibling1))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupSibling1))
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupSibling1)
+
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupSibling1)
+    }
+
+    @Test
+    fun testTransferIsolatedChildAlert_withGroupAlertAll() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupChild1))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupChild1)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupChild1))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupChild1))
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupChild1)
+
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupChild1)
+    }
+
+    @Test
+    fun testTransferTwoIsolatedChildAlert_withGroupAlertSummary() {
+        // WHEN a notification should HUN and its inflation is finished
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupSibling1)
+        mCollectionListener.onEntryAdded(mGroupSibling2)
+        val entryList = listOf(mGroupSibling1, mGroupSibling2)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupSibling1)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        // THEN we tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testTransferTwoIsolatedChildAlert_withGroupAlertAll() {
+        // WHEN a notification should HUN and its inflation is finished
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2, mGroupPriority))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupChild1)
+        mCollectionListener.onEntryAdded(mGroupChild2)
+        val entryList = listOf(mGroupChild1, mGroupChild2)
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupChild1)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+
+        // THEN we tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupChild1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
+    }
+
+    @Test
+    fun testTransferToPriorityOnAddWithTwoSiblings() {
+        // WHEN a notification should HUN and its inflation is finished
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupPriority)
+        mCollectionListener.onEntryAdded(mGroupSibling1)
+        mCollectionListener.onEntryAdded(mGroupSibling2)
+
+        val beforeTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+        val afterTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+            .build()
+        mBeforeFinalizeFilterListener
+            .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupPriority)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        // THEN we tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupPriority)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testTransferToPriorityOnUpdateWithTwoSiblings() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+        mCollectionListener.onEntryUpdated(mGroupSummary)
+        mCollectionListener.onEntryUpdated(mGroupPriority)
+        mCollectionListener.onEntryUpdated(mGroupSibling1)
+        mCollectionListener.onEntryUpdated(mGroupSibling2)
+
+        val beforeTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+        val afterTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+            .build()
+        mBeforeFinalizeFilterListener
+            .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupPriority)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupPriority)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testTransferToPriorityOnUpdateWithTwoNonUpdatedSiblings() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+        mCollectionListener.onEntryUpdated(mGroupSummary)
+        mCollectionListener.onEntryUpdated(mGroupPriority)
+
+        val beforeTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+        val afterTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+            .build()
+        mBeforeFinalizeFilterListener
+            .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
+        finishBind(mGroupPriority)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
+        verify(mHeadsUpManager).showNotification(mGroupPriority)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testNoTransferToPriorityOnUpdateOfTwoSiblings() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+
+        mCollectionListener.onEntryUpdated(mGroupSummary)
+        mCollectionListener.onEntryUpdated(mGroupSibling1)
+        mCollectionListener.onEntryUpdated(mGroupSibling2)
+
+        val beforeTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+
+        val afterTransformGroup = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+            .build()
+        mBeforeFinalizeFilterListener
+            .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+
+        finishBind(mGroupSummary)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupPriority), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        verify(mHeadsUpManager).showNotification(mGroupSummary)
+        verify(mHeadsUpManager, never()).showNotification(mGroupPriority)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testNoTransferTwoChildAlert_withGroupAlertSummary() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupSibling1)
+        mCollectionListener.onEntryAdded(mGroupSibling2)
+        val groupEntry = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+        finishBind(mGroupSummary)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+
+        verify(mHeadsUpManager).showNotification(mGroupSummary)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+    }
+
+    @Test
+    fun testNoTransferTwoChildAlert_withGroupAlertAll() {
+        setShouldHeadsUp(mGroupSummary)
+        whenever(mNotifPipeline.allNotifs)
+            .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2))
+
+        mCollectionListener.onEntryAdded(mGroupSummary)
+        mCollectionListener.onEntryAdded(mGroupChild1)
+        mCollectionListener.onEntryAdded(mGroupChild2)
+        val groupEntry = GroupEntryBuilder()
+            .setSummary(mGroupSummary)
+            .setChildren(listOf(mGroupChild1, mGroupChild2))
+            .build()
+        mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+        mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+        finishBind(mGroupSummary)
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild1), any())
+        verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+
+        verify(mHeadsUpManager).showNotification(mGroupSummary)
+        verify(mHeadsUpManager, never()).showNotification(mGroupChild1)
+        verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
+    }
+
+    private fun setShouldHeadsUp(entry: NotificationEntry, should: Boolean = true) {
+        whenever(mNotificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should)
+    }
+
+    private fun finishBind(entry: NotificationEntry) {
+        verify(mHeadsUpManager, never()).showNotification(entry)
+        withArgCaptor<BindCallback> {
+            verify(mHeadsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+        }.onBindFinished(entry)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 5df1d28..17b3b1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+import com.android.systemui.statusbar.notification.collection.render.NotifPanelEventSource;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -65,9 +66,11 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
     @Mock private HeadsUpManager mHeadsUpManager;
+    @Mock private NotifPanelEventSource mNotifPanelEventSource;
 
     @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
+    @Captor private ArgumentCaptor<NotifPanelEventSource.Callbacks> mNotifPanelEventsCallbackCaptor;
     @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
 
     private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -75,6 +78,7 @@
 
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
     private StatusBarStateController.StateListener mStatusBarStateListener;
+    private NotifPanelEventSource.Callbacks mNotifPanelEventsCallback;
     private NotifStabilityManager mNotifStabilityManager;
     private NotificationEntry mEntry;
 
@@ -83,11 +87,12 @@
         MockitoAnnotations.initMocks(this);
 
         mCoordinator = new VisualStabilityCoordinator(
+                mFakeExecutor,
                 mDumpManager,
                 mHeadsUpManager,
-                mWakefulnessLifecycle,
+                mNotifPanelEventSource,
                 mStatusBarStateController,
-                mFakeExecutor);
+                mWakefulnessLifecycle);
 
         mCoordinator.attach(mNotifPipeline);
 
@@ -98,6 +103,9 @@
         verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
         mStatusBarStateListener = mSBStateListenerCaptor.getValue();
 
+        verify(mNotifPanelEventSource).registerCallbacks(mNotifPanelEventsCallbackCaptor.capture());
+        mNotifPanelEventsCallback = mNotifPanelEventsCallbackCaptor.getValue();
+
         verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());
         mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue();
         mNotifStabilityManager.setInvalidationListener(mInvalidateListener);
@@ -319,6 +327,58 @@
     }
 
     @Test
+    public void testNotLaunchingActivityAnymore_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c animation is playing
+        setActivityLaunching(true);
+
+        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
+
+        // WHEN the animation has stopped playing
+        setActivityLaunching(false);
+
+        // invalidate is called, b/c we were previously suppressing the pipeline from running
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
+    public void testNotCollapsingPanelAnymore_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c animation is playing
+        setPanelCollapsing(true);
+
+        assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
+
+        // WHEN the animation has stopped playing
+        setPanelCollapsing(false);
+
+        // invalidate is called, b/c we were previously suppressing the pipeline from running
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
+    public void testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
+        // GIVEN animation is playing
+        setPanelCollapsing(true);
+
+        // WHEN the animation has stopped playing
+        setPanelCollapsing(false);
+
+        // THEN invalidate is not called, b/c nothing has been suppressed
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
+    public void testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
+        // GIVEN animation is playing
+        setActivityLaunching(true);
+
+        // WHEN the animation has stopped playing
+        setActivityLaunching(false);
+
+        // THEN invalidate is not called, b/c nothing has been suppressed
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
     public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
         // GIVEN visual stability is being maintained b/c panel is expanded
         setPulsing(false);
@@ -370,6 +430,14 @@
 
     }
 
+    private void setActivityLaunching(boolean activityLaunching) {
+        mNotifPanelEventsCallback.onLaunchingActivityChanged(activityLaunching);
+    }
+
+    private void setPanelCollapsing(boolean collapsing) {
+        mNotifPanelEventsCallback.onPanelCollapsingChanged(collapsing);
+    }
+
     private void setPulsing(boolean pulsing) {
         mStatusBarStateListener.onPulsingChanged(pulsing);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
new file mode 100644
index 0000000..7b10f5a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.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.statusbar.notification.interruption;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.core.os.CancellationSignal;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class HeadsUpViewBinderTest extends SysuiTestCase {
+    private HeadsUpViewBinder mViewBinder;
+    @Mock private NotificationMessagingUtil mNotificationMessagingUtil;
+    @Mock private RowContentBindStage mBindStage;
+    @Mock private HeadsUpViewBinderLogger mLogger;
+    @Mock private NotificationEntry mEntry;
+    @Mock private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mViewBinder = new HeadsUpViewBinder(mNotificationMessagingUtil, mBindStage, mLogger);
+        when(mEntry.getKey()).thenReturn("key");
+        when(mEntry.getRow()).thenReturn(mRow);
+        when(mBindStage.getStageParams(eq(mEntry))).thenReturn(new RowContentBindParams());
+    }
+
+    @Test
+    public void testLoggingWorks() {
+        AtomicReference<NotifBindPipeline.BindCallback> callback = new AtomicReference<>();
+        when(mBindStage.requestRebind(any(), any())).then(i -> {
+            callback.set(i.getArgument(1));
+            return new CancellationSignal();
+        });
+
+        mViewBinder.bindHeadsUpView(mEntry, null);
+        verify(mLogger, times(1)).startBindingHun(eq("key"));
+        verify(mLogger, times(0)).entryBoundSuccessfully(eq("key"));
+        verify(mLogger, times(0)).currentOngoingBindingAborted(eq("key"));
+
+        callback.get().onBindFinished(mEntry);
+
+        verify(mLogger, times(1)).entryBoundSuccessfully(eq("key"));
+        mViewBinder.bindHeadsUpView(mEntry, null);
+
+        callback.get().onBindFinished(mEntry);
+
+        verify(mLogger, times(2)).startBindingHun(eq("key"));
+        verify(mLogger, times(2)).entryBoundSuccessfully(eq("key"));
+        verify(mLogger, times(1)).currentOngoingBindingAborted(eq("key"));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
new file mode 100644
index 0000000..0909ff2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.R
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for {@link MediaContainView}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class MediaContainerViewTest : SysuiTestCase() {
+
+    lateinit var mediaContainerView : MediaContainerView
+
+    @Before
+    fun setUp() {
+        mediaContainerView = LayoutInflater.from(context).inflate(
+                R.layout.keyguard_media_container, null, false) as MediaContainerView
+    }
+
+    @Test
+    fun testUpdateClipping_updatesClipHeight() {
+        assertTrue(mediaContainerView.clipHeight == 0)
+
+        mediaContainerView.actualHeight = 10
+        mediaContainerView.updateClipping()
+        assertTrue(mediaContainerView.clipHeight == 10)
+    }
+}
\ No newline at end of file
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 86a705f..c4f954f 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
@@ -138,6 +138,7 @@
     @Mock private ShadeController mShadeController;
     @Mock private InteractionJankMonitor mJankMonitor;
     @Mock private StackStateLogger mStackLogger;
+    @Mock private NotificationStackScrollLogger mLogger;
 
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -193,7 +194,8 @@
                 mVisualStabilityManager,
                 mShadeController,
                 mJankMonitor,
-                mStackLogger
+                mStackLogger,
+                mLogger
         );
 
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 46ba097..bdcbbbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -27,6 +27,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
@@ -44,6 +45,7 @@
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.MathUtils;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
@@ -165,6 +167,47 @@
     }
 
     @Test
+    public void testUpdateStackEndHeight_forEndOfStackHeightAnimation() {
+        final float nsslHeight = 10f;
+        final float bottomMargin = 1f;
+        final float topPadding = 1f;
+
+        mStackScroller.updateStackEndHeight(nsslHeight, bottomMargin, topPadding);
+        final float stackEndHeight = nsslHeight - bottomMargin - topPadding;
+        assertTrue(mAmbientState.getStackEndHeight() == stackEndHeight);
+    }
+
+    @Test
+    public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() {
+        final float dozeAmount = 0.5f;
+        mAmbientState.setDozeAmount(dozeAmount);
+
+        final float endHeight = 8f;
+        final float expansionFraction = 1f;
+        float expected = MathUtils.lerp(
+                endHeight * StackScrollAlgorithm.START_FRACTION,
+                endHeight, dozeAmount);
+
+        mStackScroller.updateStackHeight(endHeight, expansionFraction);
+        assertTrue(mAmbientState.getStackHeight() == expected);
+    }
+
+    @Test
+    public void testUpdateStackHeight_withExpansionAmount_whenDozeNotChanging() {
+        final float dozeAmount = 1f;
+        mAmbientState.setDozeAmount(dozeAmount);
+
+        final float endHeight = 8f;
+        final float expansionFraction = 0.5f;
+        final float expected = MathUtils.lerp(
+                endHeight * StackScrollAlgorithm.START_FRACTION,
+                endHeight, expansionFraction);
+
+        mStackScroller.updateStackHeight(endHeight, expansionFraction);
+        assertTrue(mAmbientState.getStackHeight() == expected);
+    }
+
+    @Test
     public void testNotDimmedOnKeyguard() {
         when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
         mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 5ca1f21..8c7d22d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -107,6 +108,8 @@
     private StatusBarStateController mStatusBarStateController;
     @Mock
     private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    @Mock
+    private SessionTracker mSessionTracker;
     private BiometricUnlockController mBiometricUnlockController;
 
     @Before
@@ -129,7 +132,8 @@
                 mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
                 mMetricsLogger, mDumpManager, mPowerManager,
                 mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
-                mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController);
+                mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
+                mSessionTracker);
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index ac32b9d..47f15a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -59,6 +59,13 @@
         return createEntry(id, tag, true, groupAlertBehavior);
     }
 
+    public NotificationEntry createSummaryNotification(
+            int groupAlertBehavior, int id, String tag, long when) {
+        NotificationEntry entry = createSummaryNotification(groupAlertBehavior, id, tag);
+        entry.getSbn().getNotification().when = when;
+        return entry;
+    }
+
     public NotificationEntry createChildNotification() {
         return createChildNotification(Notification.GROUP_ALERT_ALL);
     }
@@ -71,6 +78,13 @@
         return createEntry(id, tag, false, groupAlertBehavior);
     }
 
+    public NotificationEntry createChildNotification(
+            int groupAlertBehavior, int id, String tag, long when) {
+        NotificationEntry entry = createChildNotification(groupAlertBehavior, id, tag);
+        entry.getSbn().getNotification().when = when;
+        return entry;
+    }
+
     public NotificationEntry createEntry(int id, String tag, boolean isSummary,
             int groupAlertBehavior) {
         Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 6364d2f..48b1732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.R;
 import com.android.internal.view.RotationPolicy;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
new file mode 100644
index 0000000..a57f6a1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.statusbar.policy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.CommunalStateController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+import javax.inject.Provider
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
+    @Mock
+    private lateinit var screenLifecycle: ScreenLifecycle
+
+    @Mock
+    private lateinit var userSwitcherController: UserSwitcherController
+
+    @Mock
+    private lateinit var communalStateController: CommunalStateController
+
+    @Mock
+    private lateinit var keyguardStateController: KeyguardStateController
+
+    @Mock
+    private lateinit var falsingManager: FalsingManager
+
+    @Mock
+    private lateinit var configurationController: ConfigurationController
+
+    @Mock
+    private lateinit var statusBarStateController: SysuiStatusBarStateController
+
+    @Mock
+    private lateinit var dozeParameters: DozeParameters
+
+    @Mock
+    private lateinit var userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>
+
+    @Mock
+    private lateinit var screenOffAnimationController: ScreenOffAnimationController
+
+    @Mock
+    private lateinit var featureFlags: FeatureFlags
+
+    @Mock
+    private lateinit var userSwitchDialogController: UserSwitchDialogController
+
+    @Mock
+    private lateinit var uiEventLogger: UiEventLogger
+
+    @Mock
+    private lateinit var notificationPanelViewController: NotificationPanelViewController
+
+    private lateinit var view: FrameLayout
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+
+        view = LayoutInflater.from(context)
+                .inflate(R.layout.keyguard_qs_user_switch, null) as FrameLayout
+
+        keyguardQsUserSwitchController = KeyguardQsUserSwitchController(
+                view,
+                context,
+                context.resources,
+                screenLifecycle,
+                userSwitcherController,
+                communalStateController,
+                keyguardStateController,
+                falsingManager,
+                configurationController,
+                statusBarStateController,
+                dozeParameters,
+                userDetailViewAdapterProvider,
+                screenOffAnimationController,
+                featureFlags,
+                userSwitchDialogController,
+                uiEventLogger)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+        keyguardQsUserSwitchController
+                .setNotificationPanelViewController(notificationPanelViewController)
+        `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
+        `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
+        keyguardQsUserSwitchController.init()
+    }
+
+    @After
+    fun tearDown() {
+        ViewUtils.detachView(view)
+    }
+
+    @Test
+    fun testUiEventLogged() {
+        view.findViewById<View>(R.id.kg_multi_user_avatar)?.performClick()
+        verify(uiEventLogger, times(1))
+                .log(LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index c9462d6..b380553 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -33,7 +33,6 @@
 import android.media.session.MediaSession;
 import android.os.Handler;
 import android.os.Process;
-import android.os.Vibrator;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.accessibility.AccessibilityManager;
@@ -43,6 +42,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.util.RingerModeLiveData;
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -57,8 +57,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Optional;
-
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 @TestableLooper.RunWithLooper
@@ -81,7 +79,7 @@
     @Mock
     private NotificationManager mNotificationManager;
     @Mock
-    private Vibrator mVibrator;
+    private VibratorHelper mVibrator;
     @Mock
     private IAudioService mIAudioService;
     @Mock
@@ -110,7 +108,7 @@
         mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
         mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
                 mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
-                mNotificationManager, Optional.of(mVibrator), mIAudioService, mAccessibilityManager,
+                mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
                 mPackageManager, mWakefullnessLifcycle, mCallback);
         mVolumeController.setEnableDialogs(true, true);
     }
@@ -181,7 +179,7 @@
                 ThreadFactory theadFactory,
                 AudioManager audioManager,
                 NotificationManager notificationManager,
-                Optional<Vibrator> optionalVibrator,
+                VibratorHelper optionalVibrator,
                 IAudioService iAudioService,
                 AccessibilityManager accessibilityManager,
                 PackageManager packageManager,
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index e89dda9..177b080 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -282,6 +282,10 @@
     // Notify the user to set up dream
     NOTE_SETUP_DREAM = 68;
 
+    // Inform the user that MTE override is active.
+    // Package: android
+    NOTE_MTE_OVERRIDE_ENABLED = 69;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index aa42e8d..1cff374 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -284,7 +284,9 @@
         }
         final Session session = mSessions.get(sessionId);
         if (session != null && uid == session.uid) {
-            session.setAuthenticationResultLocked(data, authenticationId);
+            synchronized (session.mLock) {
+                session.setAuthenticationResultLocked(data, authenticationId);
+            }
         }
     }
 
@@ -374,7 +376,9 @@
                 + " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly;
         mMaster.logRequestLocked(historyItem);
 
-        newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
+        synchronized (newSession.mLock) {
+            newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
+        }
 
         if (forAugmentedAutofillOnly) {
             // Must embed the flag in the response, at the high-end side of the long.
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a4bf52a..095c1fc 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -17,6 +17,7 @@
 package com.android.server.autofill;
 
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
@@ -149,9 +150,10 @@
 
     private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
 
+    final Object mLock;
+
     private final AutofillManagerServiceImpl mService;
     private final Handler mHandler;
-    private final Object mLock;
     private final AutoFillUI mUi;
 
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -428,6 +430,10 @@
         /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
         @GuardedBy("mLock")
         private boolean mClientSuggestionsEnabled;
+
+        /** Whether the fill dialog UI is disabled. */
+        @GuardedBy("mLock")
+        private boolean mFillDialogDisabled;
     }
 
     /**
@@ -860,7 +866,7 @@
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
                 && remoteRenderService != null
-                && isViewFocusedLocked(flags)) {
+                && (isViewFocusedLocked(flags) || (isRequestFromActivityStarted(flags)))) {
             final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
             if (mSessionFlags.mClientSuggestionsEnabled) {
                 final int finalRequestId = requestId;
@@ -906,6 +912,10 @@
         requestAssistStructureLocked(requestId, flags);
     }
 
+    private boolean isRequestFromActivityStarted(int flags) {
+        return (flags & FLAG_ACTIVITY_START) != 0;
+    }
+
     @GuardedBy("mLock")
     private void requestAssistStructureLocked(int requestId, int flags) {
         try {
@@ -1501,6 +1511,23 @@
                 this, intentSender, intent));
     }
 
+    // AutoFillUiCallback
+    @Override
+    public void requestShowSoftInput(AutofillId id) {
+        IAutoFillManagerClient client = getClient();
+        if (client != null) {
+            try {
+                client.requestShowSoftInput(id);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error sending input show up notification", e);
+            }
+        }
+        synchronized (Session.this.mLock) {
+            // stop to show fill dialog
+            mSessionFlags.mFillDialogDisabled = true;
+        }
+    }
+
     private void notifyFillUiHidden(@NonNull AutofillId autofillId) {
         synchronized (mLock) {
             try {
@@ -2869,6 +2896,9 @@
                 // View is triggering autofill.
                 mCurrentViewId = viewState.id;
                 viewState.update(value, virtualBounds, flags);
+                if (!isRequestFromActivityStarted(flags)) {
+                    mSessionFlags.mFillDialogDisabled = true;
+                }
                 requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
                 break;
             case ACTION_VALUE_CHANGED:
@@ -2958,6 +2988,7 @@
                 }
 
                 if (isSameViewEntered) {
+                    setFillDialogDisabledAndStartInput();
                     return;
                 }
 
@@ -2968,6 +2999,7 @@
                 if (Objects.equals(mCurrentViewId, viewState.id)) {
                     if (sVerbose) Slog.v(TAG, "Exiting view " + id);
                     mUi.hideFillUi(this);
+                    mUi.hideFillDialog(this);
                     hideAugmentedAutofillLocked(viewState);
                     // We don't send an empty response to IME so that it doesn't cause UI flicker
                     // on the IME side if it arrives before the input view is finished on the IME.
@@ -3148,6 +3180,17 @@
             return;
         }
 
+        if (requestShowFillDialog(response, filledId, filterText)) {
+            synchronized (mLock) {
+                final ViewState currentView = mViewStates.get(mCurrentViewId);
+                currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
+                mService.logDatasetShown(id, mClientState);
+            }
+            return;
+        }
+
+        setFillDialogDisabled();
+
         if (response.supportsInlineSuggestions()) {
             synchronized (mLock) {
                 if (requestShowInlineSuggestionsLocked(response, filterText)) {
@@ -3192,6 +3235,81 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void updateFillDialogTriggerIdsLocked() {
+        final FillResponse response = getLastResponseLocked(null);
+
+        if (response == null) return;
+
+        final AutofillId[] ids = response.getFillDialogTriggerIds();
+        notifyClientFillDialogTriggerIds(ids == null ? null : Arrays.asList(ids));
+    }
+
+    private void notifyClientFillDialogTriggerIds(List<AutofillId> fieldIds) {
+        try {
+            if (sVerbose) {
+                Slog.v(TAG, "notifyFillDialogTriggerIds(): " + fieldIds);
+            }
+            getClient().notifyFillDialogTriggerIds(fieldIds);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Cannot set trigger ids for fill dialog", e);
+        }
+    }
+
+    private boolean isFillDialogUiEnabled() {
+        // TODO read from Settings or somewhere
+        final boolean isSettingsEnabledFillDialog = true;
+        synchronized (Session.this.mLock) {
+            return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled;
+        }
+    }
+
+    private void setFillDialogDisabled() {
+        synchronized (mLock) {
+            mSessionFlags.mFillDialogDisabled = true;
+        }
+        notifyClientFillDialogTriggerIds(null);
+    }
+
+    private void setFillDialogDisabledAndStartInput() {
+        if (getUiForShowing().isFillDialogShowing()) {
+            setFillDialogDisabled();
+            final AutofillId id;
+            synchronized (mLock) {
+                id = mCurrentViewId;
+            }
+            requestShowSoftInput(id);
+        }
+    }
+
+    private boolean requestShowFillDialog(FillResponse response,
+            AutofillId filledId, String filterText) {
+        if (!isFillDialogUiEnabled()) {
+            // Unsupported fill dialog UI
+            return false;
+        }
+
+        final AutofillId[] ids = response.getFillDialogTriggerIds();
+        if (ids == null || !ArrayUtils.contains(ids, filledId)) {
+            return false;
+        }
+
+        final Drawable serviceIcon = getServiceIcon();
+
+        getUiForShowing().showFillDialog(filledId, response, filterText,
+                mService.getServicePackageName(), mComponentName, serviceIcon, this);
+        return true;
+    }
+
+    @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
+                                   // actually the same object as mLock.
+                                   // TODO: Expose mService.mLock or redesign instead.
+    private Drawable getServiceIcon() {
+        synchronized (mLock) {
+            return mService.getServiceIconLocked();
+        }
+    }
+
     /**
      * Returns whether we made a request to show inline suggestions.
      */
@@ -3584,9 +3702,10 @@
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if (remoteRenderService != null
                 && (mSessionFlags.mAugmentedAutofillOnly
-                || !mSessionFlags.mInlineSupportedByService
-                || mSessionFlags.mExpiredResponse)
-                && isViewFocusedLocked(flags)) {
+                        || !mSessionFlags.mInlineSupportedByService
+                        || mSessionFlags.mExpiredResponse)
+                && isViewFocusedLocked(flags)
+                || isFillDialogUiEnabled()) {
             if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
             remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
                     (extras) -> {
@@ -3642,6 +3761,7 @@
         mClientState = newClientState != null ? newClientState : newResponse.getClientState();
 
         setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
+        updateFillDialogTriggerIdsLocked();
         updateTrackedIdsLocked();
 
         if (mCurrentViewId == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index adb1e3e..4a14f14 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -82,6 +82,8 @@
     public static final int STATE_INLINE_DISABLED = 0x8000;
     /** The View is waiting for an inline suggestions request from IME.*/
     public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000;
+    /** Fill dialog were shown for this View. */
+    public static final int STATE_FILL_DIALOG_SHOWN = 0x20000;
 
     public final AutofillId id;
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 71c3c16..056ab92 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -66,6 +66,7 @@
 
     private @Nullable FillUi mFillUi;
     private @Nullable SaveUi mSaveUi;
+    private @Nullable DialogFillUi mFillDialog;
 
     private @Nullable AutoFillUiCallback mCallback;
 
@@ -90,6 +91,7 @@
         void startIntentSender(IntentSender intentSender, Intent intent);
         void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
         void cancelSession();
+        void requestShowSoftInput(AutofillId id);
     }
 
     public AutoFillUI(@NonNull Context context) {
@@ -155,6 +157,12 @@
     }
 
     /**
+     * Hides the fill UI.
+     */
+    public void hideFillDialog(@NonNull AutoFillUiCallback callback) {
+        mHandler.post(() -> hideFillDialogUiThread(callback));
+    }
+    /**
      * Filters the options in the fill UI.
      *
      * @param filterText The filter prefix.
@@ -369,6 +377,62 @@
     }
 
     /**
+     * Shows the UI asking the user to choose for autofill.
+     */
+    public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
+            @Nullable String filterText, @Nullable String servicePackageName,
+            @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
+            @NonNull AutoFillUiCallback callback) {
+        if (sVerbose) {
+            Slog.v(TAG, "showFillDialog for "
+                    + componentName.toShortString() + ": " + response);
+        }
+
+        // TODO: enable LogMaker
+
+        mHandler.post(() -> {
+            if (callback != mCallback) {
+                return;
+            }
+            hideAllUiThread(callback);
+            mFillDialog = new DialogFillUi(mContext, response, focusedId, filterText,
+                    serviceIcon, servicePackageName, componentName, mOverlayControl,
+                    mUiModeMgr.isNightMode(), new DialogFillUi.UiCallback() {
+                        @Override
+                        public void onResponsePicked(FillResponse response) {
+                            hideFillDialogUiThread(callback);
+                            if (mCallback != null) {
+                                mCallback.authenticate(response.getRequestId(),
+                                        AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
+                                        response.getAuthentication(), response.getClientState(),
+                                        /* authenticateInline= */ false);
+                            }
+                        }
+
+                        @Override
+                        public void onDatasetPicked(Dataset dataset) {
+                            hideFillDialogUiThread(callback);
+                            if (mCallback != null) {
+                                final int datasetIndex = response.getDatasets().indexOf(dataset);
+                                mCallback.fill(response.getRequestId(), datasetIndex, dataset);
+                            }
+                        }
+
+                        @Override
+                        public void onCanceled() {
+                            hideFillDialogUiThread(callback);
+                            callback.requestShowSoftInput(focusedId);
+                        }
+
+                        @Override
+                        public void startIntentSender(IntentSender intentSender) {
+                            mCallback.startIntentSenderAndFinishSession(intentSender);
+                        }
+                    });
+        });
+    }
+
+    /**
      * Executes an operation in the pending save UI, if any.
      */
     public void onPendingSaveUi(int operation, @NonNull IBinder token) {
@@ -400,6 +464,10 @@
         return mSaveUi == null ? false : mSaveUi.isShowing();
     }
 
+    public boolean isFillDialogShowing() {
+        return mFillDialog == null ? false : mFillDialog.isShowing();
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("Autofill UI");
         final String prefix = "  ";
@@ -417,6 +485,12 @@
         } else {
             pw.print(prefix); pw.println("showsSaveUi: false");
         }
+        if (mFillDialog != null) {
+            pw.print(prefix); pw.println("showsFillDialog: true");
+            mFillDialog.dump(pw, prefix2);
+        } else {
+            pw.print(prefix); pw.println("showsFillDialog: false");
+        }
     }
 
     @android.annotation.UiThread
@@ -442,6 +516,14 @@
     }
 
     @android.annotation.UiThread
+    private void hideFillDialogUiThread(@Nullable AutoFillUiCallback callback) {
+        if (mFillDialog != null && (callback == null || callback == mCallback)) {
+            mFillDialog.destroy();
+            mFillDialog = null;
+        }
+    }
+
+    @android.annotation.UiThread
     private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) {
         if (mSaveUi == null) {
             // Calling destroySaveUiUiThread() twice is normal - it usually happens when the
@@ -475,12 +557,14 @@
     private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi,
             @Nullable AutoFillUiCallback callback, boolean notifyClient) {
         hideFillUiUiThread(callback, notifyClient);
+        hideFillDialogUiThread(callback);
         destroySaveUiUiThread(pendingSaveUi, notifyClient);
     }
 
     @android.annotation.UiThread
     private void hideAllUiThread(@Nullable AutoFillUiCallback callback) {
         hideFillUiUiThread(callback, true);
+        hideFillDialogUiThread(callback);
         final PendingUi pendingSaveUi = hideSaveUiUiThread(callback);
         if (pendingSaveUi != null && pendingSaveUi.getState() == PendingUi.STATE_FINISHED) {
             if (sDebug) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
new file mode 100644
index 0000000..e122993
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -0,0 +1,629 @@
+/*
+ * 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.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentSender;
+import android.graphics.drawable.Drawable;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.text.TextUtils;
+import android.util.PluralsMessageFormatter;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.server.autofill.AutofillManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * A dialog to show Autofill suggestions.
+ *
+ * This fill dialog UI shows as a bottom sheet style dialog. This dialog UI
+ * provides a larger area to display the suggestions, it provides a more
+ * conspicuous and efficient interface to the user. So it is easy for users
+ * to pay attention to the datasets and selecting one of them.
+ */
+final class DialogFillUi {
+
+    private static final String TAG = "DialogFillUi";
+    private static final int THEME_ID_LIGHT =
+            R.style.Theme_DeviceDefault_Light_Autofill_Save;
+    private static final int THEME_ID_DARK =
+            R.style.Theme_DeviceDefault_Autofill_Save;
+
+    interface UiCallback {
+        void onResponsePicked(@NonNull FillResponse response);
+        void onDatasetPicked(@NonNull Dataset dataset);
+        void onCanceled();
+        void startIntentSender(IntentSender intentSender);
+    }
+
+    private final @NonNull Dialog mDialog;
+    private final @NonNull OverlayControl mOverlayControl;
+    private final String mServicePackageName;
+    private final ComponentName mComponentName;
+    private final int mThemeId;
+    private final @NonNull Context mContext;
+    private final @NonNull UiCallback mCallback;
+    private final @NonNull ListView mListView;
+    private final @Nullable ItemsAdapter mAdapter;
+    private final int mVisibleDatasetsMaxCount;
+
+    private @Nullable String mFilterText;
+    private @Nullable AnnounceFilterResult mAnnounceFilterResult;
+    private boolean mDestroyed;
+
+    DialogFillUi(@NonNull Context context, @NonNull FillResponse response,
+            @NonNull AutofillId focusedViewId, @Nullable String filterText,
+            @Nullable Drawable serviceIcon, @Nullable String servicePackageName,
+            @Nullable ComponentName componentName, @NonNull OverlayControl overlayControl,
+            boolean nightMode, @NonNull UiCallback callback) {
+        if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+        mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
+        mCallback = callback;
+        mOverlayControl = overlayControl;
+        mServicePackageName = servicePackageName;
+        mComponentName = componentName;
+
+        mContext = new ContextThemeWrapper(context, mThemeId);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final View decor = inflater.inflate(R.layout.autofill_fill_dialog, null);
+
+        setServiceIcon(decor, serviceIcon);
+        setHeader(decor, response);
+
+        mVisibleDatasetsMaxCount = getVisibleDatasetsMaxCount();
+
+        if (response.getAuthentication() != null) {
+            mListView = null;
+            mAdapter = null;
+            try {
+                initialAuthenticationLayout(decor, response);
+            } catch (RuntimeException e) {
+                callback.onCanceled();
+                Slog.e(TAG, "Error inflating remote views", e);
+                mDialog = null;
+                return;
+            }
+        } else {
+            final List<ViewItem> items = createDatasetItems(response, focusedViewId);
+            mAdapter = new ItemsAdapter(items);
+            mListView = decor.findViewById(R.id.autofill_dialog_list);
+            initialDatasetLayout(decor, filterText);
+        }
+
+        setDismissButton(decor);
+
+        mDialog = new Dialog(mContext, mThemeId);
+        mDialog.setContentView(decor);
+        setDialogParamsAsBottomSheet();
+
+        show();
+    }
+
+    private int getVisibleDatasetsMaxCount() {
+        if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
+            final int maxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
+            if (sVerbose) {
+                Slog.v(TAG, "overriding maximum visible datasets to " + maxCount);
+            }
+            return maxCount;
+        } else {
+            return mContext.getResources()
+                    .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
+        }
+    }
+
+    private void setDialogParamsAsBottomSheet() {
+        final Window window = mDialog.getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+        window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
+        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
+        window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
+        window.setCloseOnTouchOutside(true);
+        final WindowManager.LayoutParams params = window.getAttributes();
+        params.width = WindowManager.LayoutParams.MATCH_PARENT;
+        params.accessibilityTitle =
+                mContext.getString(R.string.autofill_picker_accessibility_title);
+        params.windowAnimations = R.style.AutofillSaveAnimation;
+    }
+
+    private void setServiceIcon(View decor, Drawable serviceIcon) {
+        if (serviceIcon == null) {
+            return;
+        }
+
+        final ImageView iconView = decor.findViewById(R.id.autofill_service_icon);
+        final int actualWidth = serviceIcon.getMinimumWidth();
+        final int actualHeight = serviceIcon.getMinimumHeight();
+        if (sDebug) {
+            Slog.d(TAG, "Adding service icon "
+                    + "(" + actualWidth + "x" + actualHeight + ")");
+        }
+        iconView.setImageDrawable(serviceIcon);
+        iconView.setVisibility(View.VISIBLE);
+    }
+
+    private void setHeader(View decor, FillResponse response) {
+        final RemoteViews presentation = response.getDialogHeader();
+        if (presentation == null) {
+            return;
+        }
+
+        final ViewGroup container = decor.findViewById(R.id.autofill_dialog_header);
+        final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+            if (pendingIntent != null) {
+                mCallback.startIntentSender(pendingIntent.getIntentSender());
+            }
+            return true;
+        };
+
+        final View content = presentation.applyWithTheme(
+                mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+        container.addView(content);
+        container.setVisibility(View.VISIBLE);
+    }
+
+    private void setDismissButton(View decor) {
+        final TextView noButton = decor.findViewById(R.id.autofill_dialog_no);
+        noButton.setOnClickListener((v) -> mCallback.onCanceled());
+    }
+
+    private void setContinueButton(View decor, View.OnClickListener listener) {
+        final TextView yesButton = decor.findViewById(R.id.autofill_dialog_yes);
+        // set "Continue" by default
+        yesButton.setText(R.string.autofill_continue_yes);
+        yesButton.setOnClickListener(listener);
+    }
+
+    private void initialAuthenticationLayout(View decor, FillResponse response) {
+        RemoteViews presentation = response.getDialogPresentation();
+        if (presentation == null) {
+            presentation = response.getPresentation();
+        }
+        if (presentation == null) {
+            throw new RuntimeException("No presentation for fill dialog authentication");
+        }
+
+        // insert authentication item under autofill_dialog_container
+        final ViewGroup container = decor.findViewById(R.id.autofill_dialog_container);
+        final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+            if (pendingIntent != null) {
+                mCallback.startIntentSender(pendingIntent.getIntentSender());
+            }
+            return true;
+        };
+        final View content = presentation.applyWithTheme(
+                mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+        container.addView(content);
+        container.setVisibility(View.VISIBLE);
+        container.setFocusable(true);
+        container.setOnClickListener(v -> mCallback.onResponsePicked(response));
+        // just single item, set up continue button
+        setContinueButton(decor, v -> mCallback.onResponsePicked(response));
+    }
+
+    private ArrayList<ViewItem> createDatasetItems(FillResponse response,
+            AutofillId focusedViewId) {
+        final int datasetCount = response.getDatasets().size();
+        if (sVerbose) {
+            Slog.v(TAG, "Number datasets: " + datasetCount + " max visible: "
+                    + mVisibleDatasetsMaxCount);
+        }
+
+        final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
+            if (pendingIntent != null) {
+                mCallback.startIntentSender(pendingIntent.getIntentSender());
+            }
+            return true;
+        };
+
+        final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
+        for (int i = 0; i < datasetCount; i++) {
+            final Dataset dataset = response.getDatasets().get(i);
+            final int index = dataset.getFieldIds().indexOf(focusedViewId);
+            if (index >= 0) {
+                RemoteViews presentation = dataset.getFieldDialogPresentation(index);
+                if (presentation == null) {
+                    Slog.w(TAG, "fallback to presentation");
+                    presentation = dataset.getFieldPresentation(index);
+                }
+                if (presentation == null) {
+                    Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+                            + "service didn't provide a presentation for it on " + dataset);
+                    continue;
+                }
+                final View view;
+                try {
+                    if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
+                    view = presentation.applyWithTheme(
+                            mContext, null, interceptionHandler, mThemeId);
+                } catch (RuntimeException e) {
+                    Slog.e(TAG, "Error inflating remote views", e);
+                    continue;
+                }
+                // TODO: Extract the shared filtering logic here and in FillUi to a common
+                //  method.
+                final Dataset.DatasetFieldFilter filter = dataset.getFilter(index);
+                Pattern filterPattern = null;
+                String valueText = null;
+                boolean filterable = true;
+                if (filter == null) {
+                    final AutofillValue value = dataset.getFieldValues().get(index);
+                    if (value != null && value.isText()) {
+                        valueText = value.getTextValue().toString().toLowerCase();
+                    }
+                } else {
+                    filterPattern = filter.pattern;
+                    if (filterPattern == null) {
+                        if (sVerbose) {
+                            Slog.v(TAG, "Explicitly disabling filter at id " + focusedViewId
+                                    + " for dataset #" + index);
+                        }
+                        filterable = false;
+                    }
+                }
+
+                items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
+            }
+        }
+        return items;
+    }
+
+    private void initialDatasetLayout(View decor, String filterText) {
+        final AdapterView.OnItemClickListener onItemClickListener =
+                (adapter, view, position, id) -> {
+                    final ViewItem vi = mAdapter.getItem(position);
+                    mCallback.onDatasetPicked(vi.dataset);
+                };
+
+        mListView.setAdapter(mAdapter);
+        mListView.setVisibility(View.VISIBLE);
+        mListView.setOnItemClickListener(onItemClickListener);
+
+        if (mAdapter.getCount() == 1) {
+            // just single item, set up continue button
+            setContinueButton(decor, (v) ->
+                    onItemClickListener.onItemClick(null, null, 0, 0));
+        }
+
+        if (filterText == null) {
+            mFilterText = null;
+        } else {
+            mFilterText = filterText.toLowerCase();
+        }
+
+        final int oldCount = mAdapter.getCount();
+        mAdapter.getFilter().filter(mFilterText, (count) -> {
+            if (mDestroyed) {
+                return;
+            }
+            if (count <= 0) {
+                if (sDebug) {
+                    final int size = mFilterText == null ? 0 : mFilterText.length();
+                    Slog.d(TAG, "No dataset matches filter with " + size + " chars");
+                }
+                mCallback.onCanceled();
+            } else {
+
+                if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
+                    mListView.setVerticalScrollBarEnabled(true);
+                    mListView.onVisibilityAggregated(true);
+                } else {
+                    mListView.setVerticalScrollBarEnabled(false);
+                }
+                if (mAdapter.getCount() != oldCount) {
+                    mListView.requestLayout();
+                }
+            }
+        });
+    }
+
+    private void show() {
+        Slog.i(TAG, "Showing fill dialog");
+        mDialog.show();
+        mOverlayControl.hideOverlays();
+    }
+
+    boolean isShowing() {
+        return mDialog.isShowing();
+    }
+
+    void hide() {
+        if (sVerbose) Slog.v(TAG, "Hiding fill dialog.");
+        try {
+            mDialog.hide();
+        } finally {
+            mOverlayControl.showOverlays();
+        }
+    }
+
+    void destroy() {
+        try {
+            if (sDebug) Slog.d(TAG, "destroy()");
+            throwIfDestroyed();
+
+            mDialog.dismiss();
+            mDestroyed = true;
+        } finally {
+            mOverlayControl.showOverlays();
+        }
+    }
+
+    private void throwIfDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("cannot interact with a destroyed instance");
+        }
+    }
+
+    @Override
+    public String toString() {
+        // TODO toString
+        return "NO TITLE";
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+
+        pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
+        pw.print(prefix); pw.print("app: "); pw.println(mComponentName.toShortString());
+        pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
+        switch (mThemeId) {
+            case THEME_ID_DARK:
+                pw.println(" (dark)");
+                break;
+            case THEME_ID_LIGHT:
+                pw.println(" (light)");
+                break;
+            default:
+                pw.println("(UNKNOWN_MODE)");
+                break;
+        }
+        final View view = mDialog.getWindow().getDecorView();
+        final int[] loc = view.getLocationOnScreen();
+        pw.print(prefix); pw.print("coordinates: ");
+            pw.print('('); pw.print(loc[0]); pw.print(','); pw.print(loc[1]); pw.print(')');
+            pw.print('(');
+                pw.print(loc[0] + view.getWidth()); pw.print(',');
+                pw.print(loc[1] + view.getHeight()); pw.println(')');
+        pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+    }
+
+    private void announceSearchResultIfNeeded() {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (mAnnounceFilterResult == null) {
+                mAnnounceFilterResult = new AnnounceFilterResult();
+            }
+            mAnnounceFilterResult.post();
+        }
+    }
+
+    // TODO: Below code copied from FullUi, Extract the shared filtering logic here
+    // and in FillUi to a common method.
+    private final class AnnounceFilterResult implements Runnable {
+        private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+        public void post() {
+            remove();
+            mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+        }
+
+        public void remove() {
+            mListView.removeCallbacks(this);
+        }
+
+        @Override
+        public void run() {
+            final int count = mListView.getAdapter().getCount();
+            final String text;
+            if (count <= 0) {
+                text = mContext.getString(R.string.autofill_picker_no_suggestions);
+            } else {
+                Map<String, Object> arguments = new HashMap<>();
+                arguments.put("count", count);
+                text = PluralsMessageFormatter.format(mContext.getResources(),
+                        arguments,
+                        R.string.autofill_picker_some_suggestions);
+            }
+            mListView.announceForAccessibility(text);
+        }
+    }
+
+    private final class ItemsAdapter extends BaseAdapter implements Filterable {
+        private @NonNull final List<ViewItem> mAllItems;
+
+        private @NonNull final List<ViewItem> mFilteredItems = new ArrayList<>();
+
+        ItemsAdapter(@NonNull List<ViewItem> items) {
+            mAllItems = Collections.unmodifiableList(new ArrayList<>(items));
+            mFilteredItems.addAll(items);
+        }
+
+        @Override
+        public Filter getFilter() {
+            return new Filter() {
+                @Override
+                protected FilterResults performFiltering(CharSequence filterText) {
+                    // No locking needed as mAllItems is final an immutable
+                    final List<ViewItem> filtered = mAllItems.stream()
+                            .filter((item) -> item.matches(filterText))
+                            .collect(Collectors.toList());
+                    final FilterResults results = new FilterResults();
+                    results.values = filtered;
+                    results.count = filtered.size();
+                    return results;
+                }
+
+                @Override
+                protected void publishResults(CharSequence constraint, FilterResults results) {
+                    final boolean resultCountChanged;
+                    final int oldItemCount = mFilteredItems.size();
+                    mFilteredItems.clear();
+                    if (results.count > 0) {
+                        @SuppressWarnings("unchecked") final List<ViewItem> items =
+                                (List<ViewItem>) results.values;
+                        mFilteredItems.addAll(items);
+                    }
+                    resultCountChanged = (oldItemCount != mFilteredItems.size());
+                    if (resultCountChanged) {
+                        announceSearchResultIfNeeded();
+                    }
+                    notifyDataSetChanged();
+                }
+            };
+        }
+
+        @Override
+        public int getCount() {
+            return mFilteredItems.size();
+        }
+
+        @Override
+        public ViewItem getItem(int position) {
+            return mFilteredItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            return getItem(position).view;
+        }
+
+        @Override
+        public String toString() {
+            return "ItemsAdapter: [all=" + mAllItems + ", filtered=" + mFilteredItems + "]";
+        }
+    }
+
+
+    /**
+     * An item for the list view - either a (clickable) dataset or a (read-only) header / footer.
+     */
+    private static class ViewItem {
+        public final @Nullable String value;
+        public final @Nullable Dataset dataset;
+        public final @NonNull View view;
+        public final @Nullable Pattern filter;
+        public final boolean filterable;
+
+        /**
+         * Default constructor.
+         *
+         * @param dataset dataset associated with the item
+         * @param filter optional filter set by the service to determine how the item should be
+         * filtered
+         * @param filterable optional flag set by the service to indicate this item should not be
+         * filtered (typically used when the dataset has value but it's sensitive, like a password)
+         * @param value dataset value
+         * @param view dataset presentation.
+         */
+        ViewItem(@NonNull Dataset dataset, @Nullable Pattern filter, boolean filterable,
+                @Nullable String value, @NonNull View view) {
+            this.dataset = dataset;
+            this.value = value;
+            this.view = view;
+            this.filter = filter;
+            this.filterable = filterable;
+        }
+
+        /**
+         * Returns whether this item matches the value input by the user so it can be included
+         * in the filtered datasets.
+         */
+        public boolean matches(CharSequence filterText) {
+            if (TextUtils.isEmpty(filterText)) {
+                // Always show item when the user input is empty
+                return true;
+            }
+            if (!filterable) {
+                // Service explicitly disabled filtering using a null Pattern.
+                return false;
+            }
+            final String constraintLowerCase = filterText.toString().toLowerCase();
+            if (filter != null) {
+                // Uses pattern provided by service
+                return filter.matcher(constraintLowerCase).matches();
+            } else {
+                // Compares it with dataset value with dataset
+                return (value == null)
+                        ? (dataset.getAuthentication() == null)
+                        : value.toLowerCase().startsWith(constraintLowerCase);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder("ViewItem:[view=")
+                    .append(view.getAutofillId());
+            final String datasetId = dataset == null ? null : dataset.getId();
+            if (datasetId != null) {
+                builder.append(", dataset=").append(datasetId);
+            }
+            if (value != null) {
+                // Cannot print value because it could contain PII
+                builder.append(", value=").append(value.length()).append("_chars");
+            }
+            if (filterable) {
+                builder.append(", filterable");
+            }
+            if (filter != null) {
+                // Filter should not have PII, but it could be a huge regexp
+                builder.append(", filter=").append(filter.pattern().length()).append("_chars");
+            }
+            return builder.append(']').toString();
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 75acf81..bb49ba0 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -39,6 +39,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 
 
 /**
@@ -61,6 +62,7 @@
     private final ArraySet<ComponentName> mAllowedActivities;
     @Nullable
     private final ArraySet<ComponentName> mBlockedActivities;
+    private Consumer<ActivityInfo> mActivityBlockedCallback;
 
     @NonNull
     final ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -81,10 +83,12 @@
             @NonNull ArraySet<UserHandle> allowedUsers,
             @Nullable Set<ComponentName> allowedActivities,
             @Nullable Set<ComponentName> blockedActivities,
-            @NonNull ActivityListener activityListener) {
+            @NonNull ActivityListener activityListener,
+            @NonNull Consumer<ActivityInfo> activityBlockedCallback) {
         mAllowedUsers = allowedUsers;
         mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
         mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+        mActivityBlockedCallback = activityBlockedCallback;
         setInterestedWindowFlags(windowFlags, systemWindowFlags);
         mActivityListener = activityListener;
     }
@@ -96,6 +100,7 @@
         for (int i = 0; i < activityCount; i++) {
             final ActivityInfo aInfo = activities.get(i);
             if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
+                mActivityBlockedCallback.accept(aInfo);
                 return false;
             }
         }
@@ -105,7 +110,11 @@
     @Override
     public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
             int systemWindowFlags) {
-        return canContainActivity(activityInfo, windowFlags, systemWindowFlags);
+        if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
+            mActivityBlockedCallback.accept(activityInfo);
+            return false;
+        }
+        return true;
     }
 
     @Override
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 95b9e58..98a5ec1 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -34,6 +34,8 @@
 import android.companion.virtual.VirtualDeviceParams;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.hardware.display.DisplayManager;
@@ -57,6 +59,7 @@
 import android.window.DisplayWindowPolicyController;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
 import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
@@ -418,7 +421,8 @@
                             getAllowedUserHandles(),
                             mParams.getAllowedActivities(),
                             mParams.getBlockedActivities(),
-                            createListenerAdapter(displayId));
+                            createListenerAdapter(displayId),
+                            activityInfo -> onActivityBlocked(displayId, activityInfo));
             mWindowPolicyControllers.put(displayId, dwpc);
             return dwpc;
         }
@@ -441,6 +445,16 @@
         }
     }
 
+    private void onActivityBlocked(int displayId, ActivityInfo activityInfo) {
+        Intent intent = BlockedAppActivity.createStreamingBlockedIntent(
+                UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo,
+                mAssociationInfo.getDisplayName());
+        mContext.startActivityAsUser(
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+                ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
+                mContext.getUser());
+    }
+
     private ArraySet<UserHandle> getAllowedUserHandles() {
         ArraySet<UserHandle> result = new ArraySet<>();
         DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index f56bfab..a8eeaf8 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -901,32 +901,20 @@
 
     /**
      * Perform the given action for each package.
-     *
-     * @param locked whether to hold the packages lock. If the lock is not held, the objects will
-     *               be iterated using a temporary data structure. In the vast majority of cases,
-     *               the lock should not have to be held. This is exposed to mirror the
-     *               functionality of the other forEach methods, for eventual migration.
      * @param action action to be performed
      */
-    public abstract void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action);
+    public abstract void forEachPackageState(Consumer<PackageStateInternal> action);
 
     /**
-     * {@link #forEachPackageState(boolean, Consumer)} but filtered to only states with packages
+     * {@link #forEachPackageState(Consumer)} but filtered to only states with packages
      * on device where {@link PackageStateInternal#getPkg()} is not null.
      */
     public abstract void forEachPackage(Consumer<AndroidPackage> action);
 
     /**
      * Perform the given action for each installed package for a user.
-     * Note that packages lock will be held while performing the actions.
      */
     public abstract void forEachInstalledPackage(
-            @NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
-
-    /**
-     * Perform the given action for each installed package for a user.
-     */
-    public abstract void forEachInstalledPackage(boolean locked,
             @NonNull Consumer<AndroidPackage> action, @UserIdInt int userId);
 
     /** Returns the list of enabled components */
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
new file mode 100644
index 0000000..db510cb
--- /dev/null
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -0,0 +1,525 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.os.IBinaryTransparencyService;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @hide
+ */
+public class BinaryTransparencyService extends SystemService {
+    private static final String TAG = "TransparencyService";
+
+    private static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
+    private static final String VBMETA_DIGEST_UNAVAILABLE = "vbmeta-digest-unavailable";
+    private static final String SYSPROP_NAME_VBETA_DIGEST = "ro.boot.vbmeta.digest";
+
+    private static final String BINARY_HASH_ERROR = "SHA256HashError";
+
+    private final Context mContext;
+    private String mVbmetaDigest;
+    private HashMap<String, String> mBinaryHashes;
+    private HashMap<String, Long> mBinaryLastUpdateTimes;
+
+    final class BinaryTransparencyServiceImpl extends IBinaryTransparencyService.Stub {
+
+        @Override
+        public String getSignedImageInfo() {
+            return mVbmetaDigest;
+        }
+
+        @Override
+        public Map getApexInfo() {
+            HashMap results = new HashMap();
+            if (!updateBinaryMeasurements()) {
+                Slog.e(TAG, "Error refreshing APEX measurements.");
+                return results;
+            }
+            PackageManager pm = mContext.getPackageManager();
+            if (pm == null) {
+                Slog.e(TAG, "Error obtaining an instance of PackageManager.");
+                return results;
+            }
+
+            for (PackageInfo packageInfo : getInstalledApexs()) {
+                results.put(packageInfo, mBinaryHashes.get(packageInfo.packageName));
+            }
+
+            return results;
+        }
+
+        @Override
+        public void onShellCommand(@Nullable FileDescriptor in,
+                                   @Nullable FileDescriptor out,
+                                   @Nullable FileDescriptor err,
+                                   @NonNull String[] args,
+                                   @Nullable ShellCallback callback,
+                                   @NonNull ResultReceiver resultReceiver) throws RemoteException {
+            (new ShellCommand() {
+
+                private int printSignedImageInfo() {
+                    final PrintWriter pw = getOutPrintWriter();
+                    boolean listAllPartitions = false;
+                    String opt;
+
+                    while ((opt = getNextOption()) != null) {
+                        switch (opt) {
+                            case "-a":
+                                listAllPartitions = true;
+                                break;
+                            default:
+                                pw.println("ERROR: Unknown option: " + opt);
+                                return 1;
+                        }
+                    }
+
+                    final String signedImageInfo = getSignedImageInfo();
+                    pw.println("Image Info:");
+                    pw.println(Build.FINGERPRINT);
+                    pw.println(signedImageInfo);
+                    pw.println("");
+
+                    if (listAllPartitions) {
+                        PackageManager pm = mContext.getPackageManager();
+                        if (pm == null) {
+                            pw.println("ERROR: Failed to obtain an instance of package manager.");
+                            return -1;
+                        }
+
+                        pw.println("Other partitions:");
+                        List<Build.Partition> buildPartitions = Build.getFingerprintedPartitions();
+                        for (Build.Partition buildPartition : buildPartitions) {
+                            pw.println("Name: " + buildPartition.getName());
+                            pw.println("Fingerprint: " + buildPartition.getFingerprint());
+                            pw.println("Build time (ms): " + buildPartition.getBuildTimeMillis());
+                        }
+                    }
+                    return 0;
+                }
+
+                private void printModuleDetails(ModuleInfo moduleInfo, final PrintWriter pw) {
+                    pw.println("--- Module Details ---");
+                    pw.println("Module name: " + moduleInfo.getName());
+                    pw.println("Module visibility: "
+                            + (moduleInfo.isHidden() ? "hidden" : "visible"));
+                }
+
+                private int printAllApexs() {
+                    final PrintWriter pw = getOutPrintWriter();
+                    boolean verbose = false;
+                    String opt;
+
+                    // refresh cache to make sure info is most up-to-date
+                    if (!updateBinaryMeasurements()) {
+                        pw.println("ERROR: Failed to refresh info for APEXs.");
+                        return -1;
+                    }
+                    if (mBinaryHashes == null || (mBinaryHashes.size() == 0)) {
+                        pw.println("ERROR: Unable to obtain apex_info at this time.");
+                        return -1;
+                    }
+
+                    while ((opt = getNextOption()) != null) {
+                        switch (opt) {
+                            case "-v":
+                                verbose = true;
+                                break;
+                            default:
+                                pw.println("ERROR: Unknown option: " + opt);
+                                return 1;
+                        }
+                    }
+
+                    PackageManager pm = mContext.getPackageManager();
+                    if (pm == null) {
+                        pw.println("ERROR: Failed to obtain an instance of package manager.");
+                        return -1;
+                    }
+
+                    pw.println("APEX Info:");
+                    for (PackageInfo packageInfo : getInstalledApexs()) {
+                        String packageName = packageInfo.packageName;
+                        pw.println(packageName + ";"
+                                + packageInfo.getLongVersionCode() + ":"
+                                + mBinaryHashes.get(packageName).toLowerCase());
+
+                        if (verbose) {
+                            pw.println("Install location: "
+                                    + packageInfo.applicationInfo.sourceDir);
+                            pw.println("Last Update Time (ms): " + packageInfo.lastUpdateTime);
+
+                            ModuleInfo moduleInfo;
+                            try {
+                                moduleInfo = pm.getModuleInfo(packageInfo.packageName, 0);
+                            } catch (PackageManager.NameNotFoundException e) {
+                                pw.println("Is A Module: False");
+                                pw.println("");
+                                continue;
+                            }
+                            pw.println("Is A Module: True");
+                            printModuleDetails(moduleInfo, pw);
+                            pw.println("");
+                        }
+                    }
+                    return 0;
+                }
+
+                private int printAllModules() {
+                    final PrintWriter pw = getOutPrintWriter();
+                    boolean verbose = false;
+                    String opt;
+
+                    // refresh cache to make sure info is most up-to-date
+                    if (!updateBinaryMeasurements()) {
+                        pw.println("ERROR: Failed to refresh info for Modules.");
+                        return -1;
+                    }
+                    if (mBinaryHashes == null || (mBinaryHashes.size() == 0)) {
+                        pw.println("ERROR: Unable to obtain module_info at this time.");
+                        return -1;
+                    }
+
+                    while ((opt = getNextOption()) != null) {
+                        switch (opt) {
+                            case "-v":
+                                verbose = true;
+                                break;
+                            default:
+                                pw.println("ERROR: Unknown option: " + opt);
+                                return 1;
+                        }
+                    }
+
+                    PackageManager pm = mContext.getPackageManager();
+                    if (pm == null) {
+                        pw.println("ERROR: Failed to obtain an instance of package manager.");
+                        return -1;
+                    }
+
+                    pw.println("Module Info:");
+                    for (ModuleInfo module : pm.getInstalledModules(PackageManager.MATCH_ALL)) {
+                        String packageName = module.getPackageName();
+                        try {
+                            PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                                    PackageManager.MATCH_APEX);
+                            pw.println(packageInfo.packageName + ";"
+                                    + packageInfo.getLongVersionCode() + ":"
+                                    + mBinaryHashes.get(packageName).toLowerCase());
+
+                            if (verbose) {
+                                pw.println("Install location: "
+                                        + packageInfo.applicationInfo.sourceDir);
+                                printModuleDetails(module, pw);
+                                pw.println("");
+                            }
+                        } catch (PackageManager.NameNotFoundException e) {
+                            pw.println(packageName);
+                            pw.println(packageName
+                                    + ";ERROR:Unable to find PackageInfo for this module.");
+                            if (verbose) {
+                                printModuleDetails(module, pw);
+                                pw.println("");
+                            }
+                            continue;
+                        }
+                    }
+                    return 0;
+                }
+
+                @Override
+                public int onCommand(String cmd) {
+                    if (cmd == null) {
+                        return handleDefaultCommands(cmd);
+                    }
+
+                    final PrintWriter pw = getOutPrintWriter();
+                    switch (cmd) {
+                        case "get": {
+                            final String infoType = getNextArg();
+                            if (infoType == null) {
+                                printHelpMenu();
+                                return -1;
+                            }
+
+                            switch (infoType) {
+                                case "image_info":
+                                    return printSignedImageInfo();
+                                case "apex_info":
+                                    return printAllApexs();
+                                case "module_info":
+                                    return printAllModules();
+                                default:
+                                    pw.println(String.format("ERROR: Unknown info type '%s'",
+                                            infoType));
+                                    return 1;
+                            }
+                        }
+                        default:
+                            return handleDefaultCommands(cmd);
+                    }
+                }
+
+                private void printHelpMenu() {
+                    final PrintWriter pw = getOutPrintWriter();
+                    pw.println("Transparency manager (transparency) commands:");
+                    pw.println("    help");
+                    pw.println("        Print this help text.");
+                    pw.println("");
+                    pw.println("    get image_info [-a]");
+                    pw.println("        Print information about loaded image (firmware). Options:");
+                    pw.println("            -a: lists all other identifiable partitions.");
+                    pw.println("");
+                    pw.println("    get apex_info [-v]");
+                    pw.println("        Print information about installed APEXs on device.");
+                    pw.println("            -v: lists more verbose information about each APEX");
+                    pw.println("");
+                    pw.println("    get module_info [-v]");
+                    pw.println("        Print information about installed modules on device.");
+                    pw.println("            -v: lists more verbose information about each module");
+                    pw.println("");
+                }
+
+                @Override
+                public void onHelp() {
+                    printHelpMenu();
+                }
+            }).exec(this, in, out, err, args, callback, resultReceiver);
+        }
+    }
+    private final BinaryTransparencyServiceImpl mServiceImpl;
+
+    public BinaryTransparencyService(Context context) {
+        super(context);
+        mContext = context;
+        mServiceImpl = new BinaryTransparencyServiceImpl();
+        mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
+        mBinaryHashes = new HashMap<>();
+        mBinaryLastUpdateTimes = new HashMap<>();
+    }
+
+    /**
+     * Called when the system service should publish a binder service using
+     * {@link #publishBinderService(String, IBinder).}
+     */
+    @Override
+    public void onStart() {
+        try {
+            publishBinderService(Context.BINARY_TRANSPARENCY_SERVICE, mServiceImpl);
+            Slog.i(TAG, "Started BinaryTransparencyService");
+        } catch (Throwable t) {
+            Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
+        }
+    }
+
+    /**
+     * Called on each phase of the boot process. Phases before the service's start phase
+     * (as defined in the @Service annotation) are never received.
+     *
+     * @param phase The current boot phase.
+     */
+    @Override
+    public void onBootPhase(int phase) {
+
+        // we are only interested in doing things at PHASE_BOOT_COMPLETED
+        if (phase == PHASE_BOOT_COMPLETED) {
+            // due to potentially long computation that holds up boot time, apex sha computations
+            // are deferred to first call
+            Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
+            getVBMetaDigestInformation();
+        }
+    }
+
+    private void getVBMetaDigestInformation() {
+        mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
+        Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
+    }
+
+    @NonNull
+    private List<PackageInfo> getInstalledApexs() {
+        List<PackageInfo> results = new ArrayList<PackageInfo>();
+        PackageManager pm = mContext.getPackageManager();
+        if (pm == null) {
+            Slog.e(TAG, "Error obtaining an instance of PackageManager.");
+            return results;
+        }
+        List<PackageInfo> allPackages = pm.getInstalledPackages(
+                PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+        if (allPackages == null) {
+            Slog.e(TAG, "Error obtaining installed packages (including APEX)");
+            return results;
+        }
+
+        results = allPackages.stream().filter(p -> p.isApex).collect(Collectors.toList());
+        return results;
+    }
+
+
+    /**
+     * Updates the internal data structure with the most current APEX measurements.
+     * @return true if update is successful; false otherwise.
+     */
+    private boolean updateBinaryMeasurements() {
+        if (mBinaryHashes.size() == 0) {
+            Slog.d(TAG, "No apex in cache yet.");
+            doFreshBinaryMeasurements();
+            return true;
+        }
+
+        PackageManager pm = mContext.getPackageManager();
+        if (pm == null) {
+            Slog.e(TAG, "Failed to obtain a valid PackageManager instance.");
+            return false;
+        }
+
+        // We're assuming updates to existing modules and APEXs can happen, but not brand new
+        // ones appearing out of the blue. Thus, we're going to only go through our cache to check
+        // for changes, rather than freshly invoking `getInstalledPackages()` and
+        // `getInstalledModules()`
+        for (Map.Entry<String, Long> entry : mBinaryLastUpdateTimes.entrySet()) {
+            String packageName = entry.getKey();
+            try {
+                PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                        PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+                long cachedUpdateTime = entry.getValue();
+
+                if (packageInfo.lastUpdateTime > cachedUpdateTime) {
+                    Slog.d(TAG, packageName + " has been updated!");
+                    entry.setValue(packageInfo.lastUpdateTime);
+
+                    // compute the digest for the updated package
+                    String sha256digest = computeSha256DigestOfFile(
+                            packageInfo.applicationInfo.sourceDir);
+                    if (sha256digest == null) {
+                        Slog.e(TAG, "Failed to compute SHA256sum for file at "
+                                + packageInfo.applicationInfo.sourceDir);
+                        mBinaryHashes.put(packageName, BINARY_HASH_ERROR);
+                    } else {
+                        mBinaryHashes.put(packageName, sha256digest);
+                    }
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.e(TAG, "Could not find package with name " + packageName);
+                continue;
+            }
+        }
+
+        return true;
+    }
+
+    private void doFreshBinaryMeasurements() {
+        PackageManager pm = mContext.getPackageManager();
+        Slog.d(TAG, "Obtained package manager");
+
+        // In general, we care about all APEXs, *and* all Modules, which may include some APKs.
+
+        // First, we deal with all installed APEXs.
+        for (PackageInfo packageInfo : getInstalledApexs()) {
+            ApplicationInfo appInfo = packageInfo.applicationInfo;
+
+            // compute SHA256 for these APEXs
+            String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+            if (sha256digest == null) {
+                Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
+                        packageInfo.packageName));
+                mBinaryHashes.put(packageInfo.packageName, BINARY_HASH_ERROR);
+            } else {
+                mBinaryHashes.put(packageInfo.packageName, sha256digest);
+            }
+            Slog.d(TAG, String.format("Last update time for %s: %d", packageInfo.packageName,
+                    packageInfo.lastUpdateTime));
+            mBinaryLastUpdateTimes.put(packageInfo.packageName, packageInfo.lastUpdateTime);
+        }
+
+        // Next, get all installed modules from PackageManager - skip over those APEXs we've
+        // processed above
+        for (ModuleInfo module : pm.getInstalledModules(PackageManager.MATCH_ALL)) {
+            String packageName = module.getPackageName();
+            if (packageName == null) {
+                Slog.e(TAG, "ERROR: Encountered null package name for module "
+                        + module.getApexModuleName());
+                continue;
+            }
+            if (mBinaryHashes.containsKey(module.getPackageName())) {
+                continue;
+            }
+
+            // get PackageInfo for this module
+            try {
+                PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                        PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
+                ApplicationInfo appInfo = packageInfo.applicationInfo;
+
+                // compute SHA256 digest for these modules
+                String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+                if (sha256digest == null) {
+                    Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
+                            packageName));
+                    mBinaryHashes.put(packageName, BINARY_HASH_ERROR);
+                } else {
+                    mBinaryHashes.put(packageName, sha256digest);
+                }
+                Slog.d(TAG, String.format("Last update time for %s: %d", packageName,
+                        packageInfo.lastUpdateTime));
+                mBinaryLastUpdateTimes.put(packageName, packageInfo.lastUpdateTime);
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.e(TAG, "ERROR: Could not obtain PackageInfo for package name: "
+                        + packageName);
+                continue;
+            }
+        }
+    }
+
+    @Nullable
+    private String computeSha256DigestOfFile(@NonNull String pathToFile) {
+        File apexFile = new File(pathToFile);
+
+        try {
+            byte[] apexFileBytes = Files.readAllBytes(apexFile.toPath());
+            return PackageUtils.computeSha256Digest(apexFileBytes);
+        } catch (IOException e) {
+            Slog.e(TAG, String.format("I/O error occurs when reading from %s", pathToFile));
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 71b463a..4129feb 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -10,9 +10,6 @@
 # Userspace reboot
 per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com
 
-# Sensor Privacy
-per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
-
 # ServiceWatcher
 per-file ServiceWatcher.java = sooniln@google.com
 
@@ -22,6 +19,7 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Battery* = file:/BATTERY_STATS_OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
 per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 16645df..06c11fa 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -673,6 +673,12 @@
                 throw new UnsupportedOperationException("cannot read frp credential");
             }
         }
+
+        @Override
+        public String getPersistentDataPackageName() {
+            enforcePersistentDataBlockAccess();
+            return mContext.getString(R.string.config_persistentDataPackageName);
+        }
     };
 
     private PersistentDataBlockManagerInternal mInternalService =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f71f02a..8aeae6a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -123,6 +123,7 @@
 import android.provider.Downloads;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.service.storage.ExternalStorageService;
 import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -491,6 +492,8 @@
     @GuardedBy("mAppFuseLock")
     private AppFuseBridge mAppFuseBridge = null;
 
+    private HashMap<Integer, Integer> mUserSharesMediaWith = new HashMap<>();
+
     /** Matches known application dir paths. The first group contains the generic part of the path,
      * the second group contains the user id (or null if it's a public volume without users), the
      * third group contains the package name, and the fourth group the remainder of the path.
@@ -1235,6 +1238,21 @@
     private void onUnlockUser(int userId) {
         Slog.d(TAG, "onUnlockUser " + userId);
 
+        if (userId != UserHandle.USER_SYSTEM) {
+            // Check if this user shares media with another user
+            try {
+                Context userContext = mContext.createPackageContextAsUser("system", 0,
+                        UserHandle.of(userId));
+                UserManager um = userContext.getSystemService(UserManager.class);
+                if (um != null && um.isMediaSharedWithParent()) {
+                    int parentUserId = um.getProfileParent(userId).id;
+                    mUserSharesMediaWith.put(userId, parentUserId);
+                    mUserSharesMediaWith.put(parentUserId, userId);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Failed to create user context for user " + userId);
+            }
+        }
         // We purposefully block here to make sure that user-specific
         // staging area is ready so it's ready for zygote-forked apps to
         // bind mount against.
@@ -3971,6 +3989,29 @@
         final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
         final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
         final boolean includeRecent = (flags & StorageManager.FLAG_INCLUDE_RECENT) != 0;
+        final boolean includeSharedProfile =
+                (flags & StorageManager.FLAG_INCLUDE_SHARED_PROFILE) != 0;
+
+        // Only Apps with MANAGE_EXTERNAL_STORAGE should call the API with includeSharedProfile
+        if (includeSharedProfile) {
+            try {
+                // Get package name for calling app and
+                // verify it has MANAGE_EXTERNAL_STORAGE permission
+                final String[] packagesFromUid = mIPackageManager.getPackagesForUid(callingUid);
+                if (packagesFromUid == null) {
+                    throw new SecurityException("Unknown uid " + callingUid);
+                }
+                // Checking first entry in packagesFromUid is enough as using "sharedUserId"
+                // mechanism is rare and discouraged. Also, Apps that share same UID share the same
+                // permissions.
+                if (!mStorageManagerInternal.hasExternalStorageAccess(callingUid,
+                        packagesFromUid[0])) {
+                    throw new SecurityException("Only File Manager Apps permitted");
+                }
+            } catch (RemoteException re) {
+                throw new SecurityException("Unknown uid " + callingUid, re);
+            }
+        }
 
         // Report all volumes as unmounted until we've recorded that user 0 has unlocked. There
         // are no guarantees that callers will see a consistent view of the volume before that
@@ -4002,6 +4043,7 @@
 
         final ArrayList<StorageVolume> res = new ArrayList<>();
         final ArraySet<String> resUuids = new ArraySet<>();
+        final int userIdSharingMedia = mUserSharesMediaWith.getOrDefault(userId, -1);
         synchronized (mLock) {
             for (int i = 0; i < mVolumes.size(); i++) {
                 final String volId = mVolumes.keyAt(i);
@@ -4014,6 +4056,11 @@
                         if (vol.getMountUserId() == userId) {
                             break;
                         }
+                        if (includeSharedProfile && vol.getMountUserId() == userIdSharingMedia) {
+                            // If the volume belongs to a user we share media with,
+                            // return it too.
+                            break;
+                        }
                         // Skip if emulated volume not for userId
                     default:
                         continue;
@@ -4021,10 +4068,12 @@
 
                 boolean match = false;
                 if (forWrite) {
-                    match = vol.isVisibleForWrite(userId);
+                    match = vol.isVisibleForWrite(userId)
+                            || (includeSharedProfile && vol.isVisibleForWrite(userIdSharingMedia));
                 } else {
                     match = vol.isVisibleForUser(userId)
-                            || (includeInvisible && vol.getPath() != null);
+                            || (includeInvisible && vol.getPath() != null)
+                            || (includeSharedProfile && vol.isVisibleForRead(userIdSharingMedia));
                 }
                 if (!match) continue;
 
@@ -4045,9 +4094,13 @@
                     reportUnmounted = true;
                 }
 
-                final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
+                int volUserId = userId;
+                if (volUserId != vol.getMountUserId() && vol.getMountUserId() >= 0) {
+                    volUserId = vol.getMountUserId();
+                }
+                final StorageVolume userVol = vol.buildStorageVolume(mContext, volUserId,
                         reportUnmounted);
-                if (vol.isPrimary()) {
+                if (vol.isPrimary() && vol.getMountUserId() == userId) {
                     res.add(0, userVol);
                     foundPrimary = true;
                 } else {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae4f79e7..902659c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5014,6 +5014,7 @@
         }
         // UART is on if init's console service is running, send a warning notification.
         showConsoleNotificationIfActive();
+        showMteOverrideNotificationIfActive();
 
         t.traceEnd();
     }
@@ -5048,6 +5049,35 @@
 
     }
 
+    private void showMteOverrideNotificationIfActive() {
+        if (!SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false)
+            || !com.android.internal.os.Zygote.nativeSupportsMemoryTagging()) {
+            return;
+        }
+        String title = mContext
+                .getString(com.android.internal.R.string.mte_override_notification_title);
+        String message = mContext
+                .getString(com.android.internal.R.string.mte_override_notification_message);
+        Notification notification =
+                new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+                        .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                        .setOngoing(true)
+                        .setTicker(title)
+                        .setDefaults(0)  // please be quiet
+                        .setColor(mContext.getColor(
+                                com.android.internal.R.color
+                                        .system_notification_accent_color))
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setVisibility(Notification.VISIBILITY_PUBLIC)
+                        .build();
+
+        NotificationManager notificationManager =
+                mContext.getSystemService(NotificationManager.class);
+        notificationManager.notifyAsUser(
+                null, SystemMessage.NOTE_MTE_OVERRIDE_ENABLED, notification, UserHandle.ALL);
+    }
+
     @Override
     public void bootAnimationComplete() {
         if (DEBUG_ALL) Slog.d(TAG, "bootAnimationComplete: Callers=" + Debug.getCallers(4));
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 8a21a0f..14d73f6 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -145,7 +145,7 @@
     /**
      * The uid battery usage stats data from our last query, it does not include snapshot data.
      */
-    // No lock is needed.
+    @GuardedBy("mLock")
     private final SparseDoubleArray mLastUidBatteryUsage = new SparseDoubleArray();
 
     // No lock is needed.
@@ -155,12 +155,15 @@
     private final SparseDoubleArray mTmpUidBatteryUsage2 = new SparseDoubleArray();
 
     // No lock is needed.
+    private final SparseDoubleArray mTmpUidBatteryUsageInWindow = new SparseDoubleArray();
+
+    // No lock is needed.
     private final ArraySet<UserHandle> mTmpUserIds = new ArraySet<>();
 
     /**
      * The start timestamp of the battery usage stats result from our last query.
      */
-    // No lock is needed.
+    @GuardedBy("mLock")
     private long mLastUidBatteryUsageStartTs;
 
     // For debug only.
@@ -296,8 +299,10 @@
             checkBatteryUsageStats();
         } else {
             // We didn't do the battery stats update above, schedule a check later.
-            scheduleBatteryUsageStatsUpdateIfNecessary(
-                    mLastBatteryUsageSamplingTs + mBatteryUsageStatsPollingMinIntervalMs - now);
+            synchronized (mLock) {
+                scheduleBatteryUsageStatsUpdateIfNecessary(
+                        mLastBatteryUsageSamplingTs + mBatteryUsageStatsPollingMinIntervalMs - now);
+            }
         }
     }
 
@@ -305,7 +310,10 @@
         final long now = SystemClock.elapsedRealtime();
         final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
         try {
-            final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
+            final SparseDoubleArray uidConsumers = mTmpUidBatteryUsageInWindow;
+            synchronized (mLock) {
+                copyUidBatteryUsage(mUidBatteryUsageInWindow, uidConsumers);
+            }
             final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
             for (int i = 0, size = uidConsumers.size(); i < size; i++) {
                 final int uid = uidConsumers.keyAt(i);
@@ -408,7 +416,9 @@
 
         if (curDuration >= windowSize) {
             // If we do have long enough data for the window, save it.
-            copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+            synchronized (mLock) {
+                copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+            }
             needUpdateUidBatteryUsageInWindow = false;
         }
 
@@ -416,8 +426,11 @@
         mTmpUidBatteryUsage2.clear();
         copyUidBatteryUsage(buf, mTmpUidBatteryUsage2);
 
-        final long lastUidBatteryUsageStartTs = mLastUidBatteryUsageStartTs;
-        mLastUidBatteryUsageStartTs = curStart;
+        final long lastUidBatteryUsageStartTs;
+        synchronized (mLock) {
+            lastUidBatteryUsageStartTs = mLastUidBatteryUsageStartTs;
+            mLastUidBatteryUsageStartTs = curStart;
+        }
         if (curStart > lastUidBatteryUsageStartTs && lastUidBatteryUsageStartTs > 0) {
             // The battery usage stats committed data since our last query,
             // let's query the snapshots to get the data since last start.
@@ -429,42 +442,47 @@
         }
         if (needUpdateUidBatteryUsageInWindow && curDuration > windowSize) {
             // If we do have long enough data for the window, save it.
-            copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+            synchronized (mLock) {
+                copyUidBatteryUsage(buf, mUidBatteryUsageInWindow, windowSize * 1.0d / curDuration);
+            }
             needUpdateUidBatteryUsageInWindow = false;
         }
 
         // Add the delta into the global records.
-        for (int i = 0, size = buf.size(); i < size; i++) {
-            final int uid = buf.keyAt(i);
-            final int index = mUidBatteryUsage.indexOfKey(uid);
-            final double delta = Math.max(0.0d,
-                    buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d));
-            final double before;
-            if (index >= 0) {
-                before = mUidBatteryUsage.valueAt(index);
-                mUidBatteryUsage.setValueAt(index, before + delta);
-            } else {
-                before = 0.0d;
-                mUidBatteryUsage.put(uid, delta);
-            }
-            if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
-                final double actualDelta = buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d);
-                String msg = "Updating mUidBatteryUsage uid=" + uid + ", before=" + before
-                        + ", after=" + mUidBatteryUsage.get(uid, 0.0d) + ", delta=" + actualDelta
-                        + ", last=" + mLastUidBatteryUsage.get(uid, 0.0d)
-                        + ", curStart=" + curStart
-                        + ", lastLastStart=" + lastUidBatteryUsageStartTs
-                        + ", thisLastStart=" + mLastUidBatteryUsageStartTs;
-                if (actualDelta < 0.0d) {
-                    // Something is wrong, the battery usage shouldn't be negative.
-                    Slog.e(TAG, msg);
+        synchronized (mLock) {
+            for (int i = 0, size = buf.size(); i < size; i++) {
+                final int uid = buf.keyAt(i);
+                final int index = mUidBatteryUsage.indexOfKey(uid);
+                final double delta = Math.max(0.0d,
+                        buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d));
+                final double before;
+                if (index >= 0) {
+                    before = mUidBatteryUsage.valueAt(index);
+                    mUidBatteryUsage.setValueAt(index, before + delta);
                 } else {
-                    Slog.i(TAG, msg);
+                    before = 0.0d;
+                    mUidBatteryUsage.put(uid, delta);
+                }
+                if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
+                    final double actualDelta = buf.valueAt(i) - mLastUidBatteryUsage.get(uid, 0.0d);
+                    String msg = "Updating mUidBatteryUsage uid=" + uid + ", before=" + before
+                            + ", after=" + mUidBatteryUsage.get(uid, 0.0d)
+                            + ", delta=" + actualDelta
+                            + ", last=" + mLastUidBatteryUsage.get(uid, 0.0d)
+                            + ", curStart=" + curStart
+                            + ", lastLastStart=" + lastUidBatteryUsageStartTs
+                            + ", thisLastStart=" + mLastUidBatteryUsageStartTs;
+                    if (actualDelta < 0.0d) {
+                        // Something is wrong, the battery usage shouldn't be negative.
+                        Slog.e(TAG, msg);
+                    } else {
+                        Slog.i(TAG, msg);
+                    }
                 }
             }
+            // Now update the mLastUidBatteryUsage with the data we just saved above.
+            copyUidBatteryUsage(mTmpUidBatteryUsage2, mLastUidBatteryUsage);
         }
-        // Now update the mLastUidBatteryUsage with the data we just saved above.
-        copyUidBatteryUsage(mTmpUidBatteryUsage2, mLastUidBatteryUsage);
         mTmpUidBatteryUsage2.clear();
 
         if (needUpdateUidBatteryUsageInWindow) {
@@ -473,7 +491,9 @@
                     .includeProcessStateData()
                     .aggregateSnapshots(now - windowSize, lastUidBatteryUsageStartTs);
             updateBatteryUsageStatsOnceInternal(buf, builder, userIds, batteryStatsInternal);
-            copyUidBatteryUsage(buf, mUidBatteryUsageInWindow);
+            synchronized (mLock) {
+                copyUidBatteryUsage(buf, mUidBatteryUsageInWindow);
+            }
         }
     }
 
@@ -584,38 +604,40 @@
         pw.print(prefix);
         pw.println("APP BATTERY STATE TRACKER:");
         updateBatteryUsageStatsIfNecessary(mInjector.currentTimeMillis(), true);
-        final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
-        pw.print("  " + prefix);
-        pw.print("Boot=");
-        TimeUtils.dumpTime(pw, mBootTimestamp);
-        pw.print("  Last battery usage start=");
-        TimeUtils.dumpTime(pw, mLastUidBatteryUsageStartTs);
-        pw.println();
-        pw.print("  " + prefix);
-        pw.print("Battery usage over last ");
-        final String newPrefix = "    " + prefix;
-        final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
-        final long now = SystemClock.elapsedRealtime();
-        final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
-        pw.println(TimeUtils.formatDuration(now - since));
-        if (uidConsumers.size() == 0) {
-            pw.print(newPrefix);
-            pw.println("(none)");
-        } else {
-            for (int i = 0, size = uidConsumers.size(); i < size; i++) {
-                final int uid = uidConsumers.keyAt(i);
-                final double bgUsage = uidConsumers.valueAt(i);
-                final double exemptedUsage = mAppRestrictionController
-                        .getUidBatteryExemptedUsageSince(uid, since, now);
-                final double reportedUsage = Math.max(0.0d, bgUsage - exemptedUsage);
-                pw.format("%s%s: [%s] %.3f mAh (%4.2f%%) | %.3f mAh (%4.2f%%) | "
-                        + "%.3f mAh (%4.2f%%) | %.3f mAh\n",
-                        newPrefix, UserHandle.formatUid(uid),
-                        PowerExemptionManager.reasonCodeToString(bgPolicy.shouldExemptUid(uid)),
-                        bgUsage , bgPolicy.getPercentage(uid, bgUsage),
-                        exemptedUsage, bgPolicy.getPercentage(-1, exemptedUsage),
-                        reportedUsage, bgPolicy.getPercentage(-1, reportedUsage),
-                        mUidBatteryUsage.get(uid, 0.0d));
+        synchronized (mLock) {
+            final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
+            pw.print("  " + prefix);
+            pw.print("Boot=");
+            TimeUtils.dumpTime(pw, mBootTimestamp);
+            pw.print("  Last battery usage start=");
+            TimeUtils.dumpTime(pw, mLastUidBatteryUsageStartTs);
+            pw.println();
+            pw.print("  " + prefix);
+            pw.print("Battery usage over last ");
+            final String newPrefix = "    " + prefix;
+            final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
+            final long now = SystemClock.elapsedRealtime();
+            final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
+            pw.println(TimeUtils.formatDuration(now - since));
+            if (uidConsumers.size() == 0) {
+                pw.print(newPrefix);
+                pw.println("(none)");
+            } else {
+                for (int i = 0, size = uidConsumers.size(); i < size; i++) {
+                    final int uid = uidConsumers.keyAt(i);
+                    final double bgUsage = uidConsumers.valueAt(i);
+                    final double exemptedUsage = mAppRestrictionController
+                            .getUidBatteryExemptedUsageSince(uid, since, now);
+                    final double reportedUsage = Math.max(0.0d, bgUsage - exemptedUsage);
+                    pw.format("%s%s: [%s] %.3f mAh (%4.2f%%) | %.3f mAh (%4.2f%%) | "
+                            + "%.3f mAh (%4.2f%%) | %.3f mAh\n",
+                            newPrefix, UserHandle.formatUid(uid),
+                            PowerExemptionManager.reasonCodeToString(bgPolicy.shouldExemptUid(uid)),
+                            bgUsage , bgPolicy.getPercentage(uid, bgUsage),
+                            exemptedUsage, bgPolicy.getPercentage(-1, exemptedUsage),
+                            reportedUsage, bgPolicy.getPercentage(-1, reportedUsage),
+                            mUidBatteryUsage.get(uid, 0.0d));
+                }
             }
         }
         super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index bd63a24..1315293 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -1314,7 +1314,7 @@
         void onSystemReady() {
             mContext.registerReceiverForAllUsers(mActionButtonReceiver,
                     new IntentFilter(ACTION_FGS_MANAGER_TRAMPOLINE),
-                    MANAGE_ACTIVITY_TASKS, mBgController.mBgHandler);
+                    MANAGE_ACTIVITY_TASKS, mBgController.mBgHandler, Context.RECEIVER_NOT_EXPORTED);
         }
 
         void postRequestBgRestrictedIfNecessary(String packageName, int uid) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 417ebcd..3c9d29d 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -83,9 +83,9 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
-import com.android.server.app.GameManagerService.GamePackageConfiguration.GameModeConfiguration;
 
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 
 /**
@@ -128,6 +128,8 @@
     private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
     @GuardedBy("mOverrideConfigLock")
     private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
+    @Nullable
+    private final GameServiceController mGameServiceController;
 
     public GameManagerService(Context context) {
         this(context, createServiceThread().getLooper());
@@ -140,6 +142,16 @@
         mPlatformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
+            mGameServiceController = new GameServiceController(
+                    BackgroundThread.getExecutor(),
+                    new GameServiceProviderSelectorImpl(
+                            context.getResources(),
+                            context.getPackageManager()),
+                    new GameServiceProviderInstanceFactoryImpl(context));
+        } else {
+            mGameServiceController = null;
+        }
     }
 
     @Override
@@ -148,6 +160,29 @@
         new GameManagerShellCommand().exec(this, in, out, err, args, callback, result);
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            writer.println("Permission Denial: can't dump GameManagerService from from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " without permission " + android.Manifest.permission.DUMP);
+            return;
+        }
+        if (args == null || args.length == 0) {
+            writer.println("*Dump GameManagerService*");
+            dumpAllGameConfigs(writer);
+        }
+    }
+
+    private void dumpAllGameConfigs(PrintWriter pw) {
+        final int userId = ActivityManager.getCurrentUser();
+        String[] packageList = getInstalledGamePackageNames(userId);
+        for (final String packageName : packageList) {
+            pw.println(getInterventionList(packageName));
+        }
+    }
+
     class SettingsHandler extends Handler {
 
         SettingsHandler(Looper looper) {
@@ -610,8 +645,6 @@
      */
     public static class Lifecycle extends SystemService {
         private GameManagerService mService;
-        @Nullable
-        private GameServiceController mGameServiceController;
 
         public Lifecycle(Context context) {
             super(context);
@@ -624,57 +657,33 @@
             publishBinderService(Context.GAME_SERVICE, mService);
             mService.registerDeviceConfigListener();
             mService.registerPackageReceiver();
-
-            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
-                mGameServiceController = new GameServiceController(
-                        BackgroundThread.getExecutor(),
-                        new GameServiceProviderSelectorImpl(
-                                getContext().getResources(),
-                                getContext().getPackageManager()),
-                        new GameServiceProviderInstanceFactoryImpl(getContext()));
-            }
         }
 
         @Override
         public void onBootPhase(int phase) {
             if (phase == PHASE_BOOT_COMPLETED) {
                 mService.onBootCompleted();
-                if (mGameServiceController != null) {
-                    mGameServiceController.onBootComplete();
-                }
             }
         }
 
         @Override
         public void onUserStarting(@NonNull TargetUser user) {
-            mService.onUserStarting(user.getUserIdentifier());
-            if (mGameServiceController != null) {
-                mGameServiceController.notifyUserStarted(user);
-            }
+            mService.onUserStarting(user);
         }
 
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
-            super.onUserUnlocking(user);
-            if (mGameServiceController != null) {
-                mGameServiceController.notifyUserUnlocking(user);
-            }
+            mService.onUserUnlocking(user);
         }
 
         @Override
         public void onUserStopping(@NonNull TargetUser user) {
-            mService.onUserStopping(user.getUserIdentifier());
-            if (mGameServiceController != null) {
-                mGameServiceController.notifyUserStopped(user);
-            }
+            mService.onUserStopping(user);
         }
 
         @Override
         public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-            mService.onUserSwitching(from, to.getUserIdentifier());
-            if (mGameServiceController != null) {
-                mGameServiceController.notifyNewForegroundUser(to);
-            }
+            mService.onUserSwitching(from, to);
         }
     }
 
@@ -868,14 +877,38 @@
     }
 
     /**
+     * Sets the game service provider to a given package, meant for testing.
+     *
+     * <p>This setting persists until the next call or until the next reboot.
+     *
+     * <p>Checks that the caller has {@link android.Manifest.permission#SET_GAME_SERVICE}.
+     */
+    @Override
+    @RequiresPermission(Manifest.permission.SET_GAME_SERVICE)
+    public void setGameServiceProvider(@Nullable String packageName) throws SecurityException {
+        checkPermission(Manifest.permission.SET_GAME_SERVICE);
+
+        if (mGameServiceController == null) {
+            return;
+        }
+
+        mGameServiceController.setGameServiceProvider(packageName);
+    }
+
+    /**
      * Notified when boot is completed.
      */
     @VisibleForTesting
     void onBootCompleted() {
         Slog.d(TAG, "onBootCompleted");
+        if (mGameServiceController != null) {
+            mGameServiceController.onBootComplete();
+        }
     }
 
-    void onUserStarting(int userId) {
+    void onUserStarting(@NonNull TargetUser user) {
+        final int userId = user.getUserIdentifier();
+
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 GameManagerSettings userSettings =
@@ -887,9 +920,21 @@
         final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
         msg.obj = userId;
         mHandler.sendMessage(msg);
+
+        if (mGameServiceController != null) {
+            mGameServiceController.notifyUserStarted(user);
+        }
     }
 
-    void onUserStopping(int userId) {
+    void onUserUnlocking(@NonNull TargetUser user) {
+        if (mGameServiceController != null) {
+            mGameServiceController.notifyUserUnlocking(user);
+        }
+    }
+
+    void onUserStopping(TargetUser user) {
+        final int userId = user.getUserIdentifier();
+
         synchronized (mLock) {
             if (!mSettings.containsKey(userId)) {
                 return;
@@ -898,9 +943,14 @@
             msg.obj = userId;
             mHandler.sendMessage(msg);
         }
+
+        if (mGameServiceController != null) {
+            mGameServiceController.notifyUserStopped(user);
+        }
     }
 
-    void onUserSwitching(TargetUser from, int toUserId) {
+    void onUserSwitching(TargetUser from, TargetUser to) {
+        final int toUserId = to.getUserIdentifier();
         if (from != null) {
             synchronized (mLock) {
                 final int fromUserId = from.getUserIdentifier();
@@ -914,6 +964,10 @@
         final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
         msg.obj = toUserId;
         mHandler.sendMessage(msg);
+
+        if (mGameServiceController != null) {
+            mGameServiceController.notifyNewForegroundUser(to);
+        }
     }
 
     /**
@@ -1236,8 +1290,7 @@
                     .append(packageName);
             return listStrSb.toString();
         }
-        listStrSb.append("\nPackage name: ")
-                .append(packageName)
+        listStrSb.append("\n")
                 .append(packageConfig.toString());
         return listStrSb.toString();
     }
diff --git a/services/core/java/com/android/server/app/GameServiceController.java b/services/core/java/com/android/server/app/GameServiceController.java
index ac720b9..397439a 100644
--- a/services/core/java/com/android/server/app/GameServiceController.java
+++ b/services/core/java/com/android/server/app/GameServiceController.java
@@ -44,6 +44,8 @@
 
     private volatile boolean mHasBootCompleted;
     @Nullable
+    private volatile String mGameServiceProviderOverride;
+    @Nullable
     private volatile SystemService.TargetUser mCurrentForegroundUser;
     @GuardedBy("mLock")
     @Nullable
@@ -108,6 +110,16 @@
         setCurrentForegroundUserAndEvaluateProvider(null);
     }
 
+    void setGameServiceProvider(@Nullable String packageName) {
+        boolean hasPackageChanged = !Objects.equals(mGameServiceProviderOverride, packageName);
+        if (!hasPackageChanged) {
+            return;
+        }
+        mGameServiceProviderOverride = packageName;
+
+        mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider);
+    }
+
     private void setCurrentForegroundUserAndEvaluateProvider(
             @Nullable SystemService.TargetUser user) {
         boolean hasUserChanged =
@@ -128,7 +140,8 @@
 
         synchronized (mLock) {
             GameServiceProviderConfiguration selectedGameServiceProviderConfiguration =
-                    mGameServiceProviderSelector.get(mCurrentForegroundUser);
+                    mGameServiceProviderSelector.get(mCurrentForegroundUser,
+                            mGameServiceProviderOverride);
 
             boolean didActiveGameServiceProviderChanged =
                     !Objects.equals(selectedGameServiceProviderConfiguration,
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 8578de7..9136219 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -330,7 +330,6 @@
                     + ") is not awaiting game session request. Ignoring.");
             return;
         }
-        mGameSessions.put(taskId, existingGameSessionRecord.withGameSessionRequested());
 
         GameSessionViewHostConfiguration gameSessionViewHostConfiguration =
                 createViewHostConfigurationForTask(taskId);
diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelector.java b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
index 51d3515..0f55b9f 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderSelector.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
@@ -30,5 +30,6 @@
      * Service provider for the given user or {@code null} if none should be used.
      */
     @Nullable
-    GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user);
+    GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user,
+            @Nullable String packageNameOverride);
 }
diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
index 54ef707..c1ad668 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
@@ -57,7 +57,8 @@
 
     @Override
     @Nullable
-    public GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user) {
+    public GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user,
+            @Nullable String packageNameOverride) {
         if (user == null) {
             return null;
         }
@@ -68,9 +69,16 @@
             return null;
         }
 
-        String gameServicePackage =
-                mResources.getString(
-                        com.android.internal.R.string.config_systemGameService);
+        int resolveInfoQueryFlags;
+        String gameServicePackage;
+        if (!TextUtils.isEmpty(packageNameOverride)) {
+            resolveInfoQueryFlags = 0;
+            gameServicePackage = packageNameOverride;
+        } else {
+            resolveInfoQueryFlags = PackageManager.MATCH_SYSTEM_ONLY;
+            gameServicePackage = mResources.getString(
+                    com.android.internal.R.string.config_systemGameService);
+        }
 
         if (TextUtils.isEmpty(gameServicePackage)) {
             Slog.w(TAG, "No game service package defined");
@@ -81,7 +89,7 @@
         List<ResolveInfo> gameServiceResolveInfos =
                 mPackageManager.queryIntentServicesAsUser(
                         new Intent(GameService.ACTION_GAME_SERVICE).setPackage(gameServicePackage),
-                        PackageManager.GET_META_DATA | PackageManager.MATCH_SYSTEM_ONLY,
+                        PackageManager.GET_META_DATA | resolveInfoQueryFlags,
                         userId);
         if (DEBUG) {
             Slog.i(TAG, "Querying package: " + gameServicePackage + " and user id: " + userId);
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 9d4d1c1..366718c 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -38,6 +38,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
+import android.apphibernation.HibernationStats;
 import android.apphibernation.IAppHibernationService;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -221,7 +222,7 @@
         }
         getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.MANAGE_APP_HIBERNATION,
-                "Caller does not have MANAGE_APP_HIBERNATION permission.");
+                "Caller did not have permission while calling " + methodName);
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
             if (!checkUserStatesExist(userId, methodName)) {
@@ -380,6 +381,46 @@
     }
 
     /**
+     * Return the stats from app hibernation for each package provided.
+     *
+     * @param packageNames the set of packages to return stats for. Returns all if null
+     * @return map from package to stats for that package
+     */
+    public Map<String, HibernationStats> getHibernationStatsForUser(
+            @Nullable Set<String> packageNames, int userId) {
+        Map<String, HibernationStats> statsMap = new ArrayMap<>();
+        String methodName = "getHibernationStatsForUser";
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
+        userId = handleIncomingUser(userId, methodName);
+        synchronized (mLock) {
+            if (!checkUserStatesExist(userId, methodName)) {
+                return statsMap;
+            }
+            final Map<String, UserLevelState> userPackageStates = mUserStates.get(userId);
+            Set<String> pkgs = packageNames != null ? packageNames : userPackageStates.keySet();
+            for (String pkgName : pkgs) {
+                if (!mPackageManagerInternal.canQueryPackage(Binder.getCallingUid(), pkgName)) {
+                    // Package not visible to caller
+                    continue;
+                }
+                if (!mGlobalHibernationStates.containsKey(pkgName)
+                        || !userPackageStates.containsKey(pkgName)) {
+                    Slog.w(TAG, String.format(
+                            "No hibernation state associated with package %s user %d. Maybe"
+                                    + "the package was uninstalled? ", pkgName, userId));
+                    continue;
+                }
+                HibernationStats stats = new HibernationStats(
+                        mGlobalHibernationStates.get(pkgName).savedByte);
+                statsMap.put(pkgName, stats);
+            }
+        }
+        return statsMap;
+    }
+
+    /**
      * Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do
      * not hold {@link #mLock} while calling this to avoid deadlock scenarios.
      */
@@ -788,6 +829,13 @@
         }
 
         @Override
+        public Map<String, HibernationStats> getHibernationStatsForUser(
+                @Nullable List<String> packageNames, int userId) {
+            Set<String> pkgsSet = packageNames != null ? new ArraySet<>(packageNames) : null;
+            return mService.getHibernationStatsForUser(pkgsSet, userId);
+        }
+
+        @Override
         public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
                 @Nullable FileDescriptor err, @NonNull String[] args,
                 @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5b76695..28508f4 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -654,7 +654,7 @@
                         final boolean canCopyIntoProfile = !hasRestriction(
                                 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
                         if (canCopyIntoProfile) {
-                            setPrimaryClipInternalLocked(
+                            setPrimaryClipInternalNoClassifyLocked(
                                     getClipboardLocked(id), clip, uid, sourcePackage);
                         }
                     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index f4c36c6..1b55257 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -412,7 +412,7 @@
         if (mLoggingEnabled) {
             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
         }
-        if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
+        if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) && !isInIdleMode()) {
             mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
                     mCurrentBrightnessMapper.getShortTermModelTimeout());
         } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
new file mode 100644
index 0000000..767b2d1
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -0,0 +1,262 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Temperature;
+import android.util.Slog;
+
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+
+import java.io.PrintWriter;
+
+/**
+ * This class monitors various conditions, such as skin temperature throttling status, and limits
+ * the allowed brightness range accordingly.
+ */
+class BrightnessThrottler {
+    private static final String TAG = "BrightnessThrottler";
+    private static final boolean DEBUG = false;
+
+    private static final int THROTTLING_INVALID = -1;
+
+    private final Injector mInjector;
+    private final Handler mHandler;
+    private BrightnessThrottlingData mThrottlingData;
+    private final Runnable mThrottlingChangeCallback;
+    private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+    private int mThrottlingStatus;
+    private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+    private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
+        BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+    BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData,
+            Runnable throttlingChangeCallback) {
+        this(new Injector(), handler, throttlingData, throttlingChangeCallback);
+    }
+
+    BrightnessThrottler(Injector injector, Handler handler, BrightnessThrottlingData throttlingData,
+            Runnable throttlingChangeCallback) {
+        mInjector = injector;
+        mHandler = handler;
+        mThrottlingData = throttlingData;
+        mThrottlingChangeCallback = throttlingChangeCallback;
+        mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
+
+        resetThrottlingData(mThrottlingData);
+    }
+
+    boolean deviceSupportsThrottling() {
+        return mThrottlingData != null;
+    }
+
+    float getBrightnessCap() {
+        return mBrightnessCap;
+    }
+
+    int getBrightnessMaxReason() {
+        return mBrightnessMaxReason;
+    }
+
+    boolean isThrottled() {
+        return mBrightnessMaxReason != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+    }
+
+    void stop() {
+        mSkinThermalStatusObserver.stopObserving();
+
+        // We're asked to stop throttling, so reset brightness restrictions.
+        mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+        mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+        // We set throttling status to an invalid value here so that we act on the first throttling
+        // value received from the thermal service after registration, even if that throttling value
+        // is THROTTLING_NONE.
+        mThrottlingStatus = THROTTLING_INVALID;
+    }
+
+    void resetThrottlingData(BrightnessThrottlingData throttlingData) {
+        stop();
+        mThrottlingData = throttlingData;
+
+        if (deviceSupportsThrottling()) {
+            mSkinThermalStatusObserver.startObserving();
+        }
+    }
+
+    private float verifyAndConstrainBrightnessCap(float brightness) {
+        if (brightness < PowerManager.BRIGHTNESS_MIN) {
+            Slog.e(TAG, "brightness " + brightness + " is lower than the minimum possible "
+                    + "brightness " + PowerManager.BRIGHTNESS_MIN);
+            brightness = PowerManager.BRIGHTNESS_MIN;
+        }
+
+        if (brightness > PowerManager.BRIGHTNESS_MAX) {
+            Slog.e(TAG, "brightness " + brightness + " is higher than the maximum possible "
+                    + "brightness " + PowerManager.BRIGHTNESS_MAX);
+            brightness = PowerManager.BRIGHTNESS_MAX;
+        }
+
+        return brightness;
+    }
+
+    private void thermalStatusChanged(@Temperature.ThrottlingStatus int newStatus) {
+        if (mThrottlingStatus != newStatus) {
+            mThrottlingStatus = newStatus;
+            updateThrottling();
+        }
+    }
+
+    private void updateThrottling() {
+        if (!deviceSupportsThrottling()) {
+            return;
+        }
+
+        float brightnessCap = PowerManager.BRIGHTNESS_MAX;
+        int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
+        if (mThrottlingStatus != THROTTLING_INVALID) {
+            // Throttling levels are sorted by increasing severity
+            for (ThrottlingLevel level : mThrottlingData.throttlingLevels) {
+                if (level.thermalStatus <= mThrottlingStatus) {
+                    brightnessCap = level.brightness;
+                    brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+                } else {
+                    // Throttling levels that are greater than the current status are irrelevant
+                    break;
+                }
+            }
+        }
+
+        if (mBrightnessCap != brightnessCap || mBrightnessMaxReason != brightnessMaxReason) {
+            mBrightnessCap = verifyAndConstrainBrightnessCap(brightnessCap);
+            mBrightnessMaxReason = brightnessMaxReason;
+
+            if (DEBUG) {
+                Slog.d(TAG, "State changed: mBrightnessCap = " + mBrightnessCap
+                        + ", mBrightnessMaxReason = "
+                        + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+            }
+
+            if (mThrottlingChangeCallback != null) {
+                mThrottlingChangeCallback.run();
+            }
+        }
+    }
+
+    void dump(PrintWriter pw) {
+        mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
+    }
+
+    private void dumpLocal(PrintWriter pw) {
+        pw.println("BrightnessThrottler:");
+        pw.println("  mThrottlingData=" + mThrottlingData);
+        pw.println("  mThrottlingStatus=" + mThrottlingStatus);
+        pw.println("  mBrightnessCap=" + mBrightnessCap);
+        pw.println("  mBrightnessMaxReason=" +
+            BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+
+        mSkinThermalStatusObserver.dump(pw);
+    }
+
+    private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
+        private final Injector mInjector;
+        private final Handler mHandler;
+
+        private IThermalService mThermalService;
+        private boolean mStarted;
+
+        SkinThermalStatusObserver(Injector injector, Handler handler) {
+            mInjector = injector;
+            mHandler = handler;
+        }
+
+        @Override
+        public void notifyThrottling(Temperature temp) {
+            if (DEBUG) {
+                Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+            }
+            mHandler.post(() -> {
+                final @Temperature.ThrottlingStatus int status = temp.getStatus();
+                thermalStatusChanged(status);
+            });
+        }
+
+        void startObserving() {
+            if (mStarted) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Thermal status observer already started");
+                }
+                return;
+            }
+            mThermalService = mInjector.getThermalService();
+            if (mThermalService == null) {
+                Slog.e(TAG, "Could not observe thermal status. Service not available");
+                return;
+            }
+            try {
+                // We get a callback immediately upon registering so there's no need to query
+                // for the current value.
+                mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+                mStarted = true;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register thermal status listener", e);
+            }
+        }
+
+        void stopObserving() {
+            if (!mStarted) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Stop skipped because thermal status observer not started");
+                }
+                return;
+            }
+            try {
+                mThermalService.unregisterThermalEventListener(this);
+                mStarted = false;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to unregister thermal status listener", e);
+            }
+            mThermalService = null;
+        }
+
+        void dump(PrintWriter writer) {
+            writer.println("  SkinThermalStatusObserver:");
+            writer.println("    mStarted: " + mStarted);
+            if (mThermalService != null) {
+                writer.println("    ThermalService available");
+            } else {
+                writer.println("    ThermalService not available");
+            }
+        }
+    }
+
+    public static class Injector {
+        public IThermalService getThermalService() {
+            return IThermalService.Stub.asInterface(
+                    ServiceManager.getService(Context.THERMAL_SERVICE));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/BrightnessUtils.java b/services/core/java/com/android/server/display/BrightnessUtils.java
new file mode 100644
index 0000000..84fa0cc
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessUtils.java
@@ -0,0 +1,83 @@
+/*
+ * 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.display;
+
+import android.util.MathUtils;
+
+/**
+ * Utility class providing functions to convert between linear and perceptual gamma space.
+ *
+ * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is a
+ * slight improvement to the typical gamma transfer function for displays whose max brightness
+ * exceeds the 120 nit reference point, but doesn't set a specific reference brightness like the PQ
+ * function does.
+ *
+ * Note that this transfer function is only valid if the display's backlight value is a linear
+ * control. If it's calibrated to be something non-linear, then a different transfer function
+ * should be used.
+ *
+ * Note: This code is based on the same class in the com.android.settingslib.display package.
+ */
+public class BrightnessUtils {
+
+    // Hybrid Log Gamma constant values
+    private static final float R = 0.5f;
+    private static final float A = 0.17883277f;
+    private static final float B = 0.28466892f;
+    private static final float C = 0.55991073f;
+
+    /**
+     * A function for converting from the gamma space into the linear space.
+     *
+     * @param val The value in the gamma space [0 .. 1.0]
+     * @return The corresponding value in the linear space [0 .. 1.0].
+     */
+    public static final float convertGammaToLinear(float val) {
+        final float ret;
+        if (val <= R) {
+            ret = MathUtils.sq(val / R);
+        } else {
+            ret = MathUtils.exp((val - C) / A) + B;
+        }
+
+        // HLG is normalized to the range [0, 12], ensure that value is within that range,
+        // it shouldn't be out of bounds.
+        final float normalizedRet = MathUtils.constrain(ret, 0, 12);
+
+        // Re-normalize to the range [0, 1]
+        // in order to derive the correct setting value.
+        return normalizedRet / 12;
+    }
+
+    /**
+     * A function for converting from the linear space into the gamma space.
+     *
+     * @param val The value in linear space [0 .. 1.0]
+     * @return The corresponding value in gamma space [0 .. 1.0]
+     */
+    public static final float convertLinearToGamma(float val) {
+        // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
+        final float normalizedVal = val * 12;
+        final float ret;
+        if (normalizedVal <= 1f) {
+            ret = MathUtils.sqrt(normalizedVal) * R;
+        } else {
+            ret = A * MathUtils.log(normalizedVal - B) + C;
+        }
+        return ret;
+    }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index f3969b1..a4a6eb4 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -33,6 +33,8 @@
 import com.android.internal.R;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.server.display.config.BrightnessThresholds;
+import com.android.server.display.config.BrightnessThrottlingMap;
+import com.android.server.display.config.BrightnessThrottlingPoint;
 import com.android.server.display.config.Density;
 import com.android.server.display.config.DisplayConfiguration;
 import com.android.server.display.config.DisplayQuirks;
@@ -43,6 +45,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.ThermalThrottling;
 import com.android.server.display.config.Thresholds;
 import com.android.server.display.config.XmlParser;
 
@@ -145,6 +148,8 @@
     private DensityMap mDensityMap;
     private String mLoadedFrom = null;
 
+    private BrightnessThrottlingData mBrightnessThrottlingData;
+
     private DisplayDeviceConfig(Context context) {
         mContext = context;
     }
@@ -424,6 +429,13 @@
         return mDensityMap;
     }
 
+    /**
+     * @return brightness throttling data configuration data for the display.
+     */
+    public BrightnessThrottlingData getBrightnessThrottlingData() {
+        return BrightnessThrottlingData.create(mBrightnessThrottlingData);
+    }
+
     @Override
     public String toString() {
         return "DisplayDeviceConfig{"
@@ -441,6 +453,7 @@
                 + ", mQuirks=" + mQuirks
                 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
                 + ", mHbmData=" + mHbmData
+                + ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
                 + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
                 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -502,6 +515,7 @@
                 loadBrightnessDefaultFromDdcXml(config);
                 loadBrightnessConstraintsFromConfigXml();
                 loadBrightnessMap(config);
+                loadBrightnessThrottlingMap(config);
                 loadHighBrightnessModeData(config);
                 loadQuirks(config);
                 loadBrightnessRamps(config);
@@ -664,6 +678,41 @@
         constrainNitsAndBacklightArrays();
     }
 
+    private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
+        final ThermalThrottling throttlingConfig = config.getThermalThrottling();
+        if (throttlingConfig == null) {
+            Slog.i(TAG, "no thermal throttling config found");
+            return;
+        }
+
+        final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
+        if (map == null) {
+            Slog.i(TAG, "no brightness throttling map found");
+            return;
+        }
+
+        final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
+        // At least 1 point is guaranteed by the display device config schema
+        List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+            new ArrayList<>(points.size());
+
+        boolean badConfig = false;
+        for (BrightnessThrottlingPoint point : points) {
+            ThermalStatus status = point.getThermalStatus();
+            if (!thermalStatusIsValid(status)) {
+                badConfig = true;
+                break;
+            }
+
+            throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
+                convertThermalStatus(status), point.getBrightness().floatValue()));
+        }
+
+        if (!badConfig) {
+            mBrightnessThrottlingData = BrightnessThrottlingData.create(throttlingLevels);
+        }
+    }
+
     private void loadBrightnessMapFromConfigXml() {
         // Use the config.xml mapping
         final Resources res = mContext.getResources();
@@ -931,6 +980,25 @@
         }
     }
 
+    private boolean thermalStatusIsValid(ThermalStatus value) {
+        if (value == null) {
+            return false;
+        }
+
+        switch (value) {
+            case none:
+            case light:
+            case moderate:
+            case severe:
+            case critical:
+            case emergency:
+            case shutdown:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
         if (value == null) {
             return PowerManager.THERMAL_STATUS_NONE;
@@ -1061,4 +1129,91 @@
                     + "} ";
         }
     }
+
+    /**
+     * Container for brightness throttling data.
+     */
+    static class BrightnessThrottlingData {
+        static class ThrottlingLevel {
+            public @PowerManager.ThermalStatus int thermalStatus;
+            public float brightness;
+
+            ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) {
+                this.thermalStatus = thermalStatus;
+                this.brightness = brightness;
+            }
+
+            @Override
+            public String toString() {
+                return "[" + thermalStatus + "," + brightness + "]";
+            }
+        }
+
+        public List<ThrottlingLevel> throttlingLevels;
+
+        static public BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels)
+        {
+            if (throttlingLevels == null || throttlingLevels.size() == 0) {
+                Slog.e(TAG, "BrightnessThrottlingData received null or empty throttling levels");
+                return null;
+            }
+
+            ThrottlingLevel prevLevel = throttlingLevels.get(0);
+            final int numLevels = throttlingLevels.size();
+            for (int i = 1; i < numLevels; i++) {
+                ThrottlingLevel thisLevel = throttlingLevels.get(i);
+
+                if (thisLevel.thermalStatus <= prevLevel.thermalStatus) {
+                    Slog.e(TAG, "brightnessThrottlingMap must be strictly increasing, ignoring "
+                            + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= "
+                            + prevLevel.thermalStatus);
+                    return null;
+                }
+
+                if (thisLevel.brightness >= prevLevel.brightness) {
+                    Slog.e(TAG, "brightnessThrottlingMap must be strictly decreasing, ignoring "
+                            + "configuration. Brightness " + thisLevel.brightness + " >= "
+                            + thisLevel.brightness);
+                    return null;
+                }
+
+                prevLevel = thisLevel;
+            }
+
+            for (ThrottlingLevel level : throttlingLevels) {
+                // Non-negative brightness values are enforced by device config schema
+                if (level.brightness > PowerManager.BRIGHTNESS_MAX) {
+                    Slog.e(TAG, "brightnessThrottlingMap contains a brightness value exceeding "
+                            + "system max. Brightness " + level.brightness + " > "
+                            + PowerManager.BRIGHTNESS_MAX);
+                    return null;
+                }
+            }
+
+            return new BrightnessThrottlingData(throttlingLevels);
+        }
+
+        static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
+            if (other == null)
+                return null;
+
+            return BrightnessThrottlingData.create(other.throttlingLevels);
+        }
+
+
+        @Override
+        public String toString() {
+            return "BrightnessThrottlingData{"
+                + "throttlingLevels:" + throttlingLevels
+                + "} ";
+        }
+
+        private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
+            throttlingLevels = new ArrayList<>(inLevels.size());
+            for (ThrottlingLevel level : inLevels) {
+                throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
+            }
+        }
+
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index a9a1f08..a9875c8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -335,7 +335,7 @@
     }
 
     private int setUserDisabledHdrTypes() {
-        final String[] userDisabledHdrTypesText = getAllArgs();
+        String[] userDisabledHdrTypesText = peekRemainingArgs();
         if (userDisabledHdrTypesText == null) {
             getErrPrintWriter().println("Error: no userDisabledHdrTypes specified");
             return 1;
@@ -351,7 +351,6 @@
             getErrPrintWriter().println("Error: invalid format of userDisabledHdrTypes");
             return 1;
         }
-
         final Context context = mService.getContext();
         final DisplayManager dm = context.getSystemService(DisplayManager.class);
         dm.setUserDisabledHdrTypes(userDisabledHdrTypes);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index ec4b91a..1f44854 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -346,6 +346,7 @@
     private boolean mAppliedTemporaryBrightness;
     private boolean mAppliedTemporaryAutoBrightnessAdjustment;
     private boolean mAppliedBrightnessBoost;
+    private boolean mAppliedThrottling;
 
     // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
     // information.
@@ -379,6 +380,8 @@
 
     private final HighBrightnessModeController mHbmController;
 
+    private final BrightnessThrottler mBrightnessThrottler;
+
     private final BrightnessSetting mBrightnessSetting;
 
     private final Runnable mOnBrightnessChangeRunnable;
@@ -538,6 +541,8 @@
 
         mHbmController = createHbmControllerLocked();
 
+        mBrightnessThrottler = createBrightnessThrottlerLocked();
+
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
 
@@ -827,6 +832,8 @@
         reloadReduceBrightColours();
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData());
+        mBrightnessThrottler.resetThrottlingData(
+                mDisplayDeviceConfig.getBrightnessThrottlingData());
     }
 
     private void sendUpdatePowerState() {
@@ -1039,6 +1046,7 @@
     private void cleanupHandlerThreadAfterStop() {
         setProximitySensorEnabled(false);
         mHbmController.stop();
+        mBrightnessThrottler.stop();
         mHandler.removeCallbacksAndMessages(null);
         if (mUnfinishedBusiness) {
             mCallbacks.releaseSuspendBlocker();
@@ -1336,14 +1344,6 @@
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
         }
 
-        // The current brightness to use has been calculated at this point (minus the adjustments
-        // like low-power and dim), and HbmController should be notified so that it can accurately
-        // calculate HDR or HBM levels. We specifically do it here instead of having HbmController
-        // listen to the brightness setting because certain brightness sources (just as an app
-        // override) are not saved to the setting, but should be reflected in HBM
-        // calculations.
-        mHbmController.onBrightnessChanged(brightnessState);
-
         if (updateScreenBrightnessSetting) {
             // Tell the rest of the system about the new brightness in case we had to change it
             // for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1390,6 +1390,28 @@
             mAppliedLowPower = false;
         }
 
+        // Apply brightness throttling after applying all other transforms
+        final float unthrottledBrightnessState = brightnessState;
+        if (mBrightnessThrottler.isThrottled()) {
+            brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+            mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+            if (!mAppliedThrottling) {
+                slowChange = false;
+            }
+            mAppliedThrottling = true;
+        } else if (mAppliedThrottling) {
+            slowChange = false;
+            mAppliedThrottling = false;
+        }
+
+        // The current brightness to use has been calculated at this point, and HbmController should
+        // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
+        // here instead of having HbmController listen to the brightness setting because certain
+        // brightness sources (such as an app override) are not saved to the setting, but should be
+        // reflected in HBM calculations.
+        mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
+                mBrightnessThrottler.getBrightnessMaxReason());
+
         // Animate the screen brightness when the screen is on or dozing.
         // Skip the animation when the screen is off or suspended or transition to/from VR.
         boolean brightnessAdjusted = false;
@@ -1441,6 +1463,8 @@
             // use instead. We still preserve the calculated brightness for Standard Dynamic Range
             // (SDR) layers, but the main brightness value will be the one for HDR.
             float sdrAnimateValue = animateValue;
+            // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
+            // done in HighBrightnessModeController.
             if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
                     && ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0
                     || (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) {
@@ -1618,7 +1642,8 @@
                     mCachedBrightnessInfo.brightnessMin.value,
                     mCachedBrightnessInfo.brightnessMax.value,
                     mCachedBrightnessInfo.hbmMode.value,
-                    mCachedBrightnessInfo.hbmTransitionPoint.value);
+                    mCachedBrightnessInfo.hbmTransitionPoint.value,
+                    mCachedBrightnessInfo.brightnessMaxReason.value);
         }
     }
 
@@ -1648,6 +1673,9 @@
             changed |=
                 mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
                         mHbmController.getTransitionPoint());
+            changed |=
+                mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+                        mBrightnessThrottler.getBrightnessMaxReason());
 
             return changed;
         }
@@ -1679,6 +1707,18 @@
                 }, mContext);
     }
 
+    private BrightnessThrottler createBrightnessThrottlerLocked() {
+        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+        final DisplayDeviceConfig.BrightnessThrottlingData data =
+                ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
+        return new BrightnessThrottler(mHandler, data,
+                () -> {
+                    sendUpdatePowerStateLocked();
+                    postBrightnessChangeRunnable();
+                });
+    }
+
     private void blockScreenOn() {
         if (mPendingScreenOnUnblocker == null) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -2346,6 +2386,8 @@
             pw.println("  mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
             pw.println("  mCachedBrightnessInfo.hbmTransitionPoint=" +
                     mCachedBrightnessInfo.hbmTransitionPoint.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMaxReason =" +
+                    mCachedBrightnessInfo.brightnessMaxReason .value);
         }
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2384,6 +2426,7 @@
         pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
         pw.println("  mAppliedDimming=" + mAppliedDimming);
         pw.println("  mAppliedLowPower=" + mAppliedLowPower);
+        pw.println("  mAppliedThrottling=" + mAppliedThrottling);
         pw.println("  mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
         pw.println("  mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
         pw.println("  mDozing=" + mDozing);
@@ -2422,6 +2465,10 @@
             mHbmController.dump(pw);
         }
 
+        if (mBrightnessThrottler != null) {
+            mBrightnessThrottler.dump(pw);
+        }
+
         pw.println();
         if (mDisplayWhiteBalanceController != null) {
             mDisplayWhiteBalanceController.dump(pw);
@@ -2702,7 +2749,9 @@
         static final int MODIFIER_DIMMED = 0x1;
         static final int MODIFIER_LOW_POWER = 0x2;
         static final int MODIFIER_HDR = 0x4;
-        static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR;
+        static final int MODIFIER_THROTTLED = 0x8;
+        static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
+            | MODIFIER_THROTTLED;
 
         // ADJUSTMENT_*
         // These things can happen at any point, even if the main brightness reason doesn't
@@ -2777,6 +2826,9 @@
             if ((modifier & MODIFIER_HDR) != 0) {
                 sb.append(" hdr");
             }
+            if ((modifier & MODIFIER_THROTTLED) != 0) {
+                sb.append(" throttled");
+            }
             int strlen = sb.length();
             if (sb.charAt(strlen - 1) == '[') {
                 sb.setLength(strlen - 2);
@@ -2813,6 +2865,8 @@
         public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
         public MutableFloat hbmTransitionPoint =
             new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+        public MutableInt brightnessMaxReason =
+            new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
 
         public boolean checkAndSetFloat(MutableFloat mf, float f) {
             if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index b3be894b..534ed5d 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -82,7 +82,15 @@
     private boolean mIsTimeAvailable = false;
     private boolean mIsAutoBrightnessEnabled = false;
     private boolean mIsAutoBrightnessOffByState = false;
+
+    // The following values are typically reported by DisplayPowerController.
+    // This value includes brightness throttling effects.
     private float mBrightness;
+    // This value excludes brightness throttling effects.
+    private float mUnthrottledBrightness;
+    private @BrightnessInfo.BrightnessMaxReason int mThrottlingReason =
+        BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+
     private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
     private boolean mIsHdrLayerPresent = false;
     private boolean mIsThermalStatusWithinLimit = true;
@@ -192,11 +200,14 @@
         }
     }
 
-    void onBrightnessChanged(float brightness) {
+    void onBrightnessChanged(float brightness, float unthrottledBrightness,
+            @BrightnessInfo.BrightnessMaxReason int throttlingReason) {
         if (!deviceSupportsHbm()) {
             return;
         }
         mBrightness = brightness;
+        mUnthrottledBrightness = unthrottledBrightness;
+        mThrottlingReason = throttlingReason;
 
         // If we are starting or ending a high brightness mode session, store the current
         // session in mRunningStartTimeMillis, or the old one in mEvents.
@@ -274,6 +285,8 @@
     private void dumpLocal(PrintWriter pw) {
         pw.println("HighBrightnessModeController:");
         pw.println("  mBrightness=" + mBrightness);
+        pw.println("  mUnthrottledBrightness=" + mUnthrottledBrightness);
+        pw.println("  mThrottlingReason=" + BrightnessInfo.briMaxReasonToString(mThrottlingReason));
         pw.println("  mCurrentMin=" + getCurrentBrightnessMin());
         pw.println("  mCurrentMax=" + getCurrentBrightnessMax());
         pw.println("  mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode)
@@ -431,6 +444,9 @@
                     + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
                     + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
                     + ", mBrightness: " + mBrightness
+                    + ", mUnthrottledBrightness: " + mUnthrottledBrightness
+                    + ", mThrottlingReason: "
+                        + BrightnessInfo.briMaxReasonToString(mThrottlingReason)
                     + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
                     + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
                     + ", events: " + mEvents);
@@ -446,31 +462,44 @@
 
     private void updateHbmMode() {
         int newHbmMode = calculateHighBrightnessMode();
-        updateHbmStats(mHbmMode, newHbmMode);
+        updateHbmStats(newHbmMode);
         if (mHbmMode != newHbmMode) {
             mHbmMode = newHbmMode;
             mHbmChangeCallback.run();
         }
     }
 
-    private void updateHbmStats(int mode, int newMode) {
+    private void updateHbmStats(int newMode) {
+        final float transitionPoint = mHbmData.transitionPoint;
         int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
         if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
-                && getHdrBrightnessValue() > mHbmData.transitionPoint) {
+                && getHdrBrightnessValue() > transitionPoint) {
             state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
-        } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+        } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT
+                && mBrightness > transitionPoint) {
             state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
         }
         if (state == mHbmStatsState) {
             return;
         }
-        mHbmStatsState = state;
 
         int reason =
                 FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN;
-        boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
-        boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+        final boolean oldHbmSv = (mHbmStatsState
+                == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT);
+        final boolean newHbmSv =
+                (state == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT);
         if (oldHbmSv && !newHbmSv) {
+            // HighBrightnessModeController (HBMC) currently supports throttling from two sources:
+            //     1. Internal, received from HBMC.SkinThermalStatusObserver.notifyThrottling()
+            //     2. External, received from HBMC.onBrightnessChanged()
+            // TODO(b/216373254): Deprecate internal throttling source
+            final boolean internalThermalThrottling = !mIsThermalStatusWithinLimit;
+            final boolean externalThermalThrottling =
+                mUnthrottledBrightness > transitionPoint && // We would've liked HBM brightness...
+                mBrightness <= transitionPoint &&           // ...but we got NBM, because of...
+                mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; // ...thermals.
+
             // If more than one conditions are flipped and turn off HBM sunlight
             // visibility, only one condition will be reported to make it simple.
             if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
@@ -483,7 +512,7 @@
                 reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
             } else if (!mIsTimeAvailable) {
                 reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
-            } else if (!mIsThermalStatusWithinLimit) {
+            } else if (internalThermalThrottling || externalThermalThrottling) {
                 reason = FrameworkStatsLog
                                  .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
             } else if (mIsHdrLayerPresent) {
@@ -496,6 +525,7 @@
         }
 
         mInjector.reportHbmStateChange(mDisplayStatsId, state, reason);
+        mHbmStatsState = state;
     }
 
     private String hbmStatsStateToString(int hbmStatsState) {
@@ -572,7 +602,7 @@
                                 >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
                 // Calling the brightness update so that we can recalculate
                 // brightness with HDR in mind.
-                onBrightnessChanged(mBrightness);
+                onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
             });
         }
     }
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index ed3b15f..1ebd1f5a 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -23,6 +23,8 @@
 /**
  * A custom animator that progressively updates a property value at
  * a given variable rate until it reaches a particular target value.
+ * The ramping at the given rate is done in the perceptual space using
+ * the HLG transfer functions.
  */
 class RampAnimator<T> {
     private final T mObject;
@@ -57,7 +59,9 @@
      * @param rate The convergence rate in units per second, or 0 to set the value immediately.
      * @return True if the target differs from the previous target.
      */
-    public boolean animateTo(float target, float rate) {
+    public boolean animateTo(float targetLinear, float rate) {
+        // Convert the target from the linear into the HLG space.
+        final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
 
         // Immediately jump to the target the first time.
         if (mFirstTime || rate <= 0) {
@@ -156,7 +160,9 @@
             final float oldCurrentValue = mCurrentValue;
             mCurrentValue = mAnimatedValue;
             if (oldCurrentValue != mCurrentValue) {
-                mProperty.setValue(mObject, mCurrentValue);
+                // Convert value from HLG into linear space for the property.
+                final float linearCurrentVal = BrightnessUtils.convertGammaToLinear(mCurrentValue);
+                mProperty.setValue(mObject, linearCurrentVal);
             }
             if (mTargetValue != mCurrentValue) {
                 postAnimationCallback();
@@ -201,14 +207,14 @@
          * If this is the first time the property is being set or if the rate is 0,
          * the value jumps directly to the target.
          *
-         * @param firstTarget The first target value.
-         * @param secondTarget The second target value.
+         * @param linearFirstTarget The first target value in linear space.
+         * @param linearSecondTarget The second target value in linear space.
          * @param rate The convergence rate in units per second, or 0 to set the value immediately.
          * @return True if either target differs from the previous target.
          */
-        public boolean animateTo(float firstTarget, float secondTarget, float rate) {
-            final boolean firstRetval = mFirst.animateTo(firstTarget, rate);
-            final boolean secondRetval = mSecond.animateTo(secondTarget, rate);
+        public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
+            final boolean firstRetval = mFirst.animateTo(linearFirstTarget, rate);
+            final boolean secondRetval = mSecond.animateTo(linearSecondTarget, rate);
             return firstRetval && secondRetval;
         }
 
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index c5210ad..d31f7c5 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -56,6 +56,7 @@
 
     private List<MotionEvent> mHandwritingBuffer;
     private InputEventReceiver mHandwritingEventReceiver;
+    private Runnable mInkWindowInitRunnable;
     private boolean mRecordingGesture;
     private int mCurrentDisplayId;
 
@@ -64,12 +65,13 @@
     private int mCurrentRequestId;
 
     @AnyThread
-    HandwritingModeController(Looper uiThreadLooper) {
+    HandwritingModeController(Looper uiThreadLooper, Runnable inkWindowInitRunnable) {
         mLooper = uiThreadLooper;
         mCurrentDisplayId = Display.INVALID_DISPLAY;
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mCurrentRequestId = 0;
+        mInkWindowInitRunnable = inkWindowInitRunnable;
     }
 
     // TODO(b/210039666): Consider moving this to MotionEvent
@@ -221,6 +223,14 @@
 
     private void onStylusEvent(MotionEvent event) {
         final int action = event.getActionMasked();
+
+        if (mInkWindowInitRunnable != null && (action == MotionEvent.ACTION_HOVER_ENTER
+                || event.getAction() == MotionEvent.ACTION_HOVER_ENTER)) {
+            // Ask IMMS to make ink window ready.
+            mInkWindowInitRunnable.run();
+            mInkWindowInitRunnable = null;
+        }
+
         if (action == MotionEvent.ACTION_UP) {
             mRecordingGesture = false;
             mHandwritingBuffer.clear();
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index f8ceb5f..c87ca92 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -223,4 +223,13 @@
         }
         return true;
     }
+
+    @AnyThread
+    void initInkWindow() {
+        try {
+            mTarget.initInkWindow();
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9a53d83..80c83e9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -162,28 +162,30 @@
                 }
 
                 @Override
-                public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+                public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
                     return Collections.emptyList();
                 }
 
                 @Override
-                public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+                public List<InputMethodInfo> getEnabledInputMethodListAsUser(
+                        @UserIdInt int userId) {
                     return Collections.emptyList();
                 }
 
                 @Override
-                public void onCreateInlineSuggestionsRequest(int userId,
+                public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                         InlineSuggestionsRequestInfo requestInfo,
                         IInlineSuggestionsRequestCallback cb) {
                 }
 
                 @Override
-                public boolean switchToInputMethod(String imeId, int userId) {
+                public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
                     return false;
                 }
 
                 @Override
-                public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+                public boolean setInputMethodEnabled(String imeId, boolean enabled,
+                        @UserIdInt int userId) {
                     return false;
                 }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a6fa78b..936b1a2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1636,7 +1636,19 @@
         mBindingController = new InputMethodBindingController(this);
         mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
                 com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
-        mHwController = new HandwritingModeController(thread.getLooper());
+        mHwController = new HandwritingModeController(thread.getLooper(),
+                new InkWindowInitializer());
+    }
+
+    private final class InkWindowInitializer implements Runnable {
+        public void run() {
+            synchronized (ImfLock.class) {
+                IInputMethodInvoker curMethod = getCurMethodLocked();
+                if (curMethod != null) {
+                    curMethod.initInkWindow();
+                }
+            }
+        }
     }
 
     @GuardedBy("ImfLock.class")
@@ -4965,28 +4977,28 @@
         }
 
         @Override
-        public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
+        public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
             return mService.getInputMethodListAsUser(userId);
         }
 
         @Override
-        public List<InputMethodInfo> getEnabledInputMethodListAsUser(int userId) {
+        public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
             return mService.getEnabledInputMethodListAsUser(userId);
         }
 
         @Override
-        public void onCreateInlineSuggestionsRequest(int userId,
+        public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                 InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
             mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
         }
 
         @Override
-        public boolean switchToInputMethod(String imeId, int userId) {
+        public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
             return mService.switchToInputMethod(imeId, userId);
         }
 
         @Override
-        public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+        public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
             return mService.setInputMethodEnabled(imeId, enabled, userId);
         }
 
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ffef803..aa1fa9b 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1239,29 +1239,29 @@
     }
 
     @Override
-    @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
-    public void setAutoGnssSuspended(boolean suspended) {
-        mContext.enforceCallingPermission(permission.AUTOMOTIVE_GNSS_CONTROLS, null);
+    @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+    public void setAutomotiveGnssSuspended(boolean suspended) {
+        mContext.enforceCallingPermission(permission.CONTROL_AUTOMOTIVE_GNSS, null);
 
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
             throw new IllegalStateException(
-                    "setAutoGnssSuspended only allowed on automotive devices");
+                    "setAutomotiveGnssSuspended only allowed on automotive devices");
         }
 
-        mGnssManagerService.setAutoGnssSuspended(suspended);
+        mGnssManagerService.setAutomotiveGnssSuspended(suspended);
     }
 
     @Override
-    @RequiresPermission(android.Manifest.permission.AUTOMOTIVE_GNSS_CONTROLS)
-    public boolean isAutoGnssSuspended() {
-        mContext.enforceCallingPermission(permission.AUTOMOTIVE_GNSS_CONTROLS, null);
+    @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
+    public boolean isAutomotiveGnssSuspended() {
+        mContext.enforceCallingPermission(permission.CONTROL_AUTOMOTIVE_GNSS, null);
 
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
             throw new IllegalStateException(
-                    "isAutoGnssSuspended only allowed on automotive devices");
+                    "isAutomotiveGnssSuspended only allowed on automotive devices");
         }
 
-        return mGnssManagerService.isAutoGnssSuspended();
+        return mGnssManagerService.isAutomotiveGnssSuspended();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 61e6d14..de8e06a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -154,6 +155,10 @@
     private boolean mIsWifiScanningEnabled = false;
     private boolean mIsWifiMainEnabled = false;
 
+    // True if BT is available for the Context Hub
+    private boolean mIsBtScanningEnabled = false;
+    private boolean mIsBtMainEnabled = false;
+
     // A hashmap used to record if a contexthub is waiting for daily query
     private Set<Integer> mMetricQueryPendingContextHubIds =
             Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
@@ -333,6 +338,25 @@
 
         }
 
+        if (mContextHubWrapper.supportsBtSettingNotifications()) {
+            sendBtSettingUpdate(true /* forceUpdate */);
+
+            BroadcastReceiver btReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())
+                            || BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals(
+                                    intent.getAction())) {
+                        sendBtSettingUpdate(false /* forceUpdate */);
+                    }
+                }
+            };
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+            filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+            mContext.registerReceiver(btReceiver, filter);
+        }
+
         scheduleDailyMetricSnapshot();
     }
 
@@ -735,6 +759,7 @@
             sendWifiSettingUpdate(true /* forceUpdate */);
             sendAirplaneModeSettingUpdate();
             sendMicrophoneDisableSettingUpdateForCurrentUser();
+            sendBtSettingUpdate(true /* forceUpdate */);
 
             mTransactionManager.onHubReset();
             queryNanoAppsInternal(contextHubId);
@@ -1133,6 +1158,39 @@
     }
 
     /**
+     * Obtains the latest BT availability setting value and notifies the Context Hub.
+     *
+     * @param forceUpdate True to force send update to the Context Hub, otherwise only send the
+     *                    update when the BT availability changes.
+     */
+    private void sendBtSettingUpdate(boolean forceUpdate) {
+        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        // Adapter may be null if BT is not supported.
+        if (adapter != null) {
+            boolean btEnabled = adapter.isEnabled();
+            boolean btScanEnabled = adapter.isBleScanAlwaysAvailable();
+            if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) {
+                mIsBtScanningEnabled = btScanEnabled;
+                mContextHubWrapper.onBtScanningSettingChanged(btScanEnabled);
+            }
+            if (forceUpdate || mIsBtMainEnabled != btEnabled) {
+                mIsBtMainEnabled = btEnabled;
+                mContextHubWrapper.onBtMainSettingChanged(btEnabled);
+            }
+        } else {
+            Log.d(TAG, "BT adapter not available. Defaulting to disabled");
+            if (mIsBtMainEnabled) {
+                mIsBtMainEnabled = false;
+                mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled);
+            }
+            if (mIsBtScanningEnabled) {
+                mIsBtScanningEnabled = false;
+                mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled);
+            }
+        }
+    }
+
+    /**
      * Obtains the latest airplane mode setting value and notifies the Context Hub.
      */
     private void sendAirplaneModeSettingUpdate() {
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 9b0b782..21ea1f6 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -243,6 +243,22 @@
     public abstract void onMicrophoneSettingChanged(boolean enabled);
 
     /**
+     * @return True if this version of the Contexthub HAL supports BT availability setting
+     * notifications.
+     */
+    public abstract boolean supportsBtSettingNotifications();
+
+    /**
+     * Notifies the Contexthub implementation of a BT main setting change.
+     */
+    public abstract void onBtMainSettingChanged(boolean enabled);
+
+    /**
+     * Notifies the Contexthub implementation of a BT scanning setting change.
+     */
+    public abstract void onBtScanningSettingChanged(boolean enabled);
+
+    /**
      * Invoked whenever a host client connects with the framework.
      *
      * @param info The host endpoint info.
@@ -409,6 +425,10 @@
             return true;
         }
 
+        public boolean supportsBtSettingNotifications() {
+            return true;
+        }
+
         public void onLocationSettingChanged(boolean enabled) {
             onSettingChanged(android.hardware.contexthub.Setting.LOCATION, enabled);
         }
@@ -432,6 +452,14 @@
             onSettingChanged(android.hardware.contexthub.Setting.WIFI_SCANNING, enabled);
         }
 
+        public void onBtMainSettingChanged(boolean enabled) {
+            onSettingChanged(android.hardware.contexthub.Setting.BT_MAIN, enabled);
+        }
+
+        public void onBtScanningSettingChanged(boolean enabled) {
+            onSettingChanged(android.hardware.contexthub.Setting.BT_SCANNING, enabled);
+        }
+
         @Override
         public void onHostEndpointConnected(HostEndpointInfo info) {
             try {
@@ -662,8 +690,14 @@
             mHub.registerCallback(contextHubId, mHidlCallbackMap.get(contextHubId));
         }
 
+        public boolean supportsBtSettingNotifications() {
+            return false;
+        }
+
         public void onWifiMainSettingChanged(boolean enabled) {}
         public void onWifiScanningSettingChanged(boolean enabled) {}
+        public void onBtMainSettingChanged(boolean enabled) {}
+        public void onBtScanningSettingChanged(boolean enabled) {}
     }
 
     private static class ContextHubWrapperV1_0 extends ContextHubWrapperHidl {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index c02411e..cd2ba39 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -753,7 +753,7 @@
      * Set whether the GnssLocationProvider is suspended. This method was added to help support
      * power management use cases on automotive devices.
      */
-    public void setAutoGnssSuspended(boolean suspended) {
+    public void setAutomotiveGnssSuspended(boolean suspended) {
         synchronized (mLock) {
             mAutomotiveSuspend = suspended;
         }
@@ -764,7 +764,7 @@
      * Return whether the GnssLocationProvider is suspended or not. This method was added to help
      * support power management use cases on automotive devices.
      */
-    public boolean isAutoGnssSuspended() {
+    public boolean isAutomotiveGnssSuspended() {
         synchronized (mLock) {
             return mAutomotiveSuspend && !mGpsEnabled;
         }
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 11fd727..0f9945c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -113,16 +113,16 @@
      * Set whether the GnssLocationProvider is suspended on the device. This method was added to
      * help support power management use cases on automotive devices.
      */
-    public void setAutoGnssSuspended(boolean suspended) {
-        mGnssLocationProvider.setAutoGnssSuspended(suspended);
+    public void setAutomotiveGnssSuspended(boolean suspended) {
+        mGnssLocationProvider.setAutomotiveGnssSuspended(suspended);
     }
 
     /**
      * Return whether the GnssLocationProvider is suspended or not. This method was added to
      * help support power management use cases on automotive devices.
      */
-    public boolean isAutoGnssSuspended() {
-        return mGnssLocationProvider.isAutoGnssSuspended();
+    public boolean isAutomotiveGnssSuspended() {
+        return mGnssLocationProvider.isAutomotiveGnssSuspended();
     }
 
     /** Retrieve the IGpsGeofenceHardware. */
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index fac7a40..8ce67a6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -98,8 +98,6 @@
 import static android.net.NetworkTemplate.MATCH_CARRIER;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
 import static android.net.NetworkTemplate.MATCH_WIFI;
-import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
-import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
@@ -168,7 +166,6 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
@@ -1324,7 +1321,7 @@
     };
 
     /**
-     * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
+     * Check {@link NetworkPolicy} against current {@link NetworkStatsManager}
      * to show visible notifications as needed.
      */
     @GuardedBy("mNetworkPoliciesSecondLock")
@@ -2322,6 +2319,18 @@
     }
 
     /**
+     * Template to match all metered carrier networks with the given IMSI.
+     *
+     * @hide
+     */
+    public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
+        Objects.requireNonNull(subscriberId);
+        return new NetworkTemplate.Builder(MATCH_CARRIER)
+                .setSubscriberIds(Set.of(subscriberId))
+                .setMeteredness(METERED_YES).build();
+    }
+
+    /**
      * Update the given {@link NetworkPolicy} based on any carrier-provided
      * defaults via {@link SubscriptionPlan} or {@link CarrierConfigManager}.
      * Leaves policy untouched if the user has modified it.
@@ -2724,7 +2733,8 @@
 
                 out.startTag(null, TAG_NETWORK_POLICY);
                 writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
-                final String subscriberId = template.getSubscriberId();
+                final String subscriberId = template.getSubscriberIds().isEmpty() ? null
+                        : template.getSubscriberIds().iterator().next();
                 if (subscriberId != null) {
                     out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId);
                 }
@@ -3101,7 +3111,7 @@
             }
             // When two normalized templates conflict, prefer the most
             // restrictive policy
-            policy.template = NetworkTemplate.normalize(policy.template, mMergedSubscriberIds);
+            policy.template = normalizeTemplate(policy.template, mMergedSubscriberIds);
             final NetworkPolicy existing = mNetworkPolicy.get(policy.template);
             if (existing == null || existing.compareTo(policy) > 0) {
                 if (existing != null) {
@@ -3112,6 +3122,46 @@
         }
     }
 
+    /**
+     * Examine the given template and normalize it.
+     * We pick the "lowest" merged subscriber as the primary
+     * for key purposes, and expand the template to match all other merged
+     * subscribers.
+     *
+     * There can be multiple merged subscriberIds for multi-SIM devices.
+     *
+     * <p>
+     * For example, given an incoming template matching B, and the currently
+     * active merge set [A,B], we'd return a new template that primarily matches
+     * A, but also matches B.
+     */
+    private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template,
+            @NonNull List<String[]> mergedList) {
+        // Now there are several types of network which uses Subscriber Id to store network
+        // information. For instance:
+        // 1. A merged carrier wifi network which has TYPE_WIFI with a Subscriber Id.
+        // 2. A typical cellular network could have TYPE_MOBILE with a Subscriber Id.
+
+        if (template.getSubscriberIds().isEmpty()) return template;
+
+        for (final String[] merged : mergedList) {
+            // TODO: Handle incompatible subscriberIds if that happens in practice.
+            for (final String subscriberId : template.getSubscriberIds()) {
+                if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) {
+                    // Requested template subscriber is part of the merged group; return
+                    // a template that matches all merged subscribers.
+                    return new NetworkTemplate.Builder(template.getMatchRule())
+                            .setWifiNetworkKeys(template.getWifiNetworkKeys())
+                            .setSubscriberIds(Set.of(merged))
+                            .setMeteredness(template.getMeteredness())
+                            .build();
+                }
+            }
+        }
+
+        return template;
+    }
+
     @Override
     public void snoozeLimit(NetworkTemplate template) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
@@ -5559,7 +5609,11 @@
         NetworkPolicy[] policies = getNetworkPolicies(mContext.getOpPackageName());
         NetworkTemplate templateCarrier = subscriber != null
                 ? buildTemplateCarrierMetered(subscriber) : null;
-        NetworkTemplate templateMobile = buildTemplateMobileAll(subscriber);
+        NetworkTemplate templateMobile = subscriber != null
+                ? new NetworkTemplate.Builder(MATCH_MOBILE)
+                .setSubscriberIds(Set.of(subscriber))
+                .setMeteredness(android.net.NetworkStats.METERED_YES)
+                .build() : null;
         for (NetworkPolicy policy : policies) {
             //  All policies loaded from disk will be carrier templates, and setting will also only
             //  set carrier templates, but we clear mobile templates just in case one is set by
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 4b999e9..9f086e6 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -49,6 +49,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.SELinuxUtil;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import dalvik.system.VMRuntime;
 
@@ -277,8 +278,8 @@
         });
     }
 
-    public void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
-            int userId, int flags) {
+    public void prepareAppDataContentsLIF(AndroidPackage pkg,
+            @Nullable PackageStateInternal pkgSetting, int userId, int flags) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
@@ -287,7 +288,7 @@
     }
 
     private void prepareAppDataContentsLeafLIF(AndroidPackage pkg,
-            @Nullable PackageSetting pkgSetting, int userId, int flags) {
+            @Nullable PackageStateInternal pkgSetting, int userId, int flags) {
         final String volumeUuid = pkg.getVolumeUuid();
         final String packageName = pkg.getPackageName();
 
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index b916de3..5b2e097 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -1237,21 +1237,25 @@
             // NOTE: this must come after all removals from data structures but before we update the
             //       cache
             if (setting.getSharedUser() != null) {
-                for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
-                    if (setting.getSharedUser().packages.valueAt(i) == setting) {
+                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+                        setting.getSharedUser().getPackageStates();
+                for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
+                    if (sharedUserPackages.valueAt(i) == setting) {
                         continue;
                     }
                     addPackageInternal(
-                            setting.getSharedUser().packages.valueAt(i), settings);
+                            sharedUserPackages.valueAt(i), settings);
                 }
             }
 
             synchronized (mCacheLock) {
                 removeAppIdFromVisibilityCache(setting.getAppId());
                 if (mShouldFilterCache != null && setting.getSharedUser() != null) {
-                    for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
+                    final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+                            setting.getSharedUser().getPackageStates();
+                    for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
                         PackageStateInternal siblingSetting =
-                                setting.getSharedUser().packages.valueAt(i);
+                                sharedUserPackages.valueAt(i);
                         if (siblingSetting == setting) {
                             continue;
                         }
@@ -1368,8 +1372,8 @@
                     callingSharedPkgSettings = null;
                 } else {
                     callingPkgSetting = null;
-                    callingSharedPkgSettings =
-                            ((PackageStateInternal) callingSetting).getSharedUser().packages;
+                    callingSharedPkgSettings = ((PackageStateInternal) callingSetting)
+                            .getSharedUser().getPackageStates();
                 }
             } else {
                 callingPkgSetting = null;
diff --git a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
new file mode 100644
index 0000000..bd12981
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.ChangedPackages;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+class ChangedPackagesTracker {
+
+    @NonNull
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    @NonNull
+    private int mChangedPackagesSequenceNumber;
+    /**
+     * List of changed [installed, removed or updated] packages.
+     * mapping from user id -> sequence number -> package name
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseArray<SparseArray<String>> mUserIdToSequenceToPackage = new SparseArray<>();
+    /**
+     * The sequence number of the last change to a package.
+     * mapping from user id -> package name -> sequence number
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers =
+            new SparseArray<>();
+
+    @Nullable
+    public ChangedPackages getChangedPackages(int sequenceNumber, @UserIdInt int userId) {
+        synchronized (mLock) {
+            if (sequenceNumber >= mChangedPackagesSequenceNumber) {
+                return null;
+            }
+            final SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
+            if (changedPackages == null) {
+                return null;
+            }
+            final List<String> packageNames =
+                    new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
+            for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
+                final String packageName = changedPackages.get(i);
+                if (packageName != null) {
+                    packageNames.add(packageName);
+                }
+            }
+            return packageNames.isEmpty()
+                    ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
+        }
+    }
+
+    int getSequenceNumber() {
+        return mChangedPackagesSequenceNumber;
+    }
+
+    void iterateAll(@NonNull BiConsumer<Integer, SparseArray<SparseArray<String>>>
+            sequenceNumberAndValues) {
+        synchronized (mLock) {
+            sequenceNumberAndValues.accept(mChangedPackagesSequenceNumber,
+                    mUserIdToSequenceToPackage);
+        }
+    }
+
+    void updateSequenceNumber(@NonNull String packageName, int[] userList) {
+        for (int i = userList.length - 1; i >= 0; --i) {
+            final int userId = userList[i];
+            SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
+            if (changedPackages == null) {
+                changedPackages = new SparseArray<>();
+                mUserIdToSequenceToPackage.put(userId, changedPackages);
+            }
+            Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
+            if (sequenceNumbers == null) {
+                sequenceNumbers = new HashMap<>();
+                mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
+            }
+            final Integer sequenceNumber = sequenceNumbers.get(packageName);
+            if (sequenceNumber != null) {
+                changedPackages.remove(sequenceNumber);
+            }
+            changedPackages.put(mChangedPackagesSequenceNumber, packageName);
+            sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
+        }
+        mChangedPackagesSequenceNumber++;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index ba003d2..aa393d2 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -616,98 +616,113 @@
     }
 
     void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
-        if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
-                : "Activity Resolver Table:", "  ", packageName,
-                dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
-            dumpState.setTitlePrinted(true);
+        synchronized (mLock) {
+            if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
+                            : "Activity Resolver Table:", "  ", packageName,
+                    dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+                dumpState.setTitlePrinted(true);
+            }
         }
     }
 
     void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
-        if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
-                : "Provider Resolver Table:", "  ", packageName,
-                dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
-            dumpState.setTitlePrinted(true);
+        synchronized (mLock) {
+            if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
+                            : "Provider Resolver Table:", "  ", packageName,
+                    dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+                dumpState.setTitlePrinted(true);
+            }
         }
     }
 
     void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
-        if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
-                : "Receiver Resolver Table:", "  ", packageName,
-                dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
-            dumpState.setTitlePrinted(true);
+        synchronized (mLock) {
+            if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
+                            : "Receiver Resolver Table:", "  ", packageName,
+                    dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+                dumpState.setTitlePrinted(true);
+            }
         }
     }
 
     void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
-        if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
-                : "Service Resolver Table:", "  ", packageName,
-                dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
-            dumpState.setTitlePrinted(true);
+        synchronized (mLock) {
+            if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
+                            : "Service Resolver Table:", "  ", packageName,
+                    dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
+                dumpState.setTitlePrinted(true);
+            }
         }
     }
 
     void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
-        boolean printedSomething = false;
-        for (ParsedProvider p : mProviders.mProviders.values()) {
-            if (packageName != null && !packageName.equals(p.getPackageName())) {
-                continue;
-            }
-            if (!printedSomething) {
-                if (dumpState.onTitlePrinted()) {
-                    pw.println();
+        synchronized (mLock) {
+            boolean printedSomething = false;
+            for (ParsedProvider p : mProviders.mProviders.values()) {
+                if (packageName != null && !packageName.equals(p.getPackageName())) {
+                    continue;
                 }
-                pw.println("Registered ContentProviders:");
-                printedSomething = true;
-            }
-            pw.print("  ");
-            ComponentName.printShortString(pw, p.getPackageName(), p.getName());
-            pw.println(":");
-            pw.print("    ");
-            pw.println(p.toString());
-        }
-        printedSomething = false;
-        for (Map.Entry<String, ParsedProvider> entry :
-                mProvidersByAuthority.entrySet()) {
-            ParsedProvider p = entry.getValue();
-            if (packageName != null && !packageName.equals(p.getPackageName())) {
-                continue;
-            }
-            if (!printedSomething) {
-                if (dumpState.onTitlePrinted()) {
-                    pw.println();
+                if (!printedSomething) {
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
+                    pw.println("Registered ContentProviders:");
+                    printedSomething = true;
                 }
-                pw.println("ContentProvider Authorities:");
-                printedSomething = true;
+                pw.print("  ");
+                ComponentName.printShortString(pw, p.getPackageName(), p.getName());
+                pw.println(":");
+                pw.print("    ");
+                pw.println(p.toString());
             }
-            pw.print("  ["); pw.print(entry.getKey()); pw.println("]:");
-            pw.print("    "); pw.println(p.toString());
+            printedSomething = false;
+            for (Map.Entry<String, ParsedProvider> entry :
+                    mProvidersByAuthority.entrySet()) {
+                ParsedProvider p = entry.getValue();
+                if (packageName != null && !packageName.equals(p.getPackageName())) {
+                    continue;
+                }
+                if (!printedSomething) {
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
+                    pw.println("ContentProvider Authorities:");
+                    printedSomething = true;
+                }
+                pw.print("  [");
+                pw.print(entry.getKey());
+                pw.println("]:");
+                pw.print("    ");
+                pw.println(p.toString());
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
 
-            if (pkg != null) {
-                pw.print("      applicationInfo=");
-                pw.println(AndroidPackageUtils.generateAppInfoWithoutState(pkg));
+                if (pkg != null) {
+                    pw.print("      applicationInfo=");
+                    pw.println(AndroidPackageUtils.generateAppInfoWithoutState(pkg));
+                }
             }
         }
     }
 
     void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
-        if (dumpState.onTitlePrinted()) pw.println();
-        pw.println("Service permissions:");
+        synchronized (mLock) {
+            if (dumpState.onTitlePrinted()) pw.println();
+            pw.println("Service permissions:");
 
-        final Iterator<Pair<ParsedService, ParsedIntentInfo>> filterIterator =
-                mServices.filterIterator();
-        while (filterIterator.hasNext()) {
-            final Pair<ParsedService, ParsedIntentInfo> pair = filterIterator.next();
-            ParsedService service = pair.first;
+            final Iterator<Pair<ParsedService, ParsedIntentInfo>> filterIterator =
+                    mServices.filterIterator();
+            while (filterIterator.hasNext()) {
+                final Pair<ParsedService, ParsedIntentInfo> pair = filterIterator.next();
+                ParsedService service = pair.first;
 
-            final String permission = service.getPermission();
-            if (permission != null) {
-                pw.print("    ");
-                pw.print(service.getComponentName().flattenToShortString());
-                pw.print(": ");
-                pw.println(permission);
+                final String permission = service.getPermission();
+                if (permission != null) {
+                    pw.print("    ");
+                    pw.print(service.getComponentName().flattenToShortString());
+                    pw.print(": ");
+                    pw.println(permission);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index fcf4a02..0ae3418 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -43,12 +43,16 @@
 import android.content.pm.VersionedPackage;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -214,7 +218,7 @@
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     String resolveExternalPackageName(AndroidPackage pkg);
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
-    String resolveInternalPackageNameLPr(String packageName, long versionCode);
+    String resolveInternalPackageName(String packageName, long versionCode);
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     String[] getPackagesForUid(int uid);
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -381,11 +385,12 @@
     String[] getSystemSharedLibraryNames();
 
     /**
-     * @return if the given package has a state and isn't filtered by visibility. Provides no
-     * guarantee that the package is in any usable state.
+     * @return the state if the given package has a state and isn't filtered by visibility.
+     * Provides no guarantee that the package is in any usable state.
      */
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
-    boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
+    @Nullable
+    PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
             @UserIdInt int userId);
 
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@@ -623,4 +628,16 @@
     @Nullable
     ArrayMap<String, ProcessInfo> getProcessesForUid(int uid);
     // End block
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @NonNull
+    WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries();
+
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId);
 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index cca1b97..691bf9f 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -133,9 +133,9 @@
 import com.android.server.pm.pkg.PackageStateImpl;
 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.SharedUserApi;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedInstrumentation;
 import com.android.server.pm.pkg.component.ParsedIntentInfo;
@@ -298,11 +298,6 @@
         public Collection<SharedUserSetting> getAllSharedUsers() {
             return mSettings.getAllSharedUsersLPw();
         }
-
-        @Nullable
-        public String getHarmfulAppWarning(@NonNull String packageName, @UserIdInt int userId) {
-            return mSettings.getHarmfulAppWarningLPr(packageName, userId);
-        }
     }
 
     private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
@@ -829,7 +824,7 @@
     }
 
     public AndroidPackage getPackage(String packageName) {
-        packageName = resolveInternalPackageNameLPr(
+        packageName = resolveInternalPackageName(
                 packageName, PackageManager.VERSION_CODE_HIGHEST);
         return mPackages.get(packageName);
     }
@@ -903,7 +898,7 @@
             int filterCallingUid, int userId) {
         // writer
         // Normalize package name to handle renamed packages and static libs
-        packageName = resolveInternalPackageNameLPr(packageName,
+        packageName = resolveInternalPackageName(packageName,
                 PackageManager.VERSION_CODE_HIGHEST);
 
         AndroidPackage p = mPackages.get(packageName);
@@ -1575,7 +1570,7 @@
             PackageInfo pi = new PackageInfo();
             pi.packageName = ps.getPackageName();
             pi.setLongVersionCode(ps.getVersionCode());
-            pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().name : null;
+            pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().getName() : null;
             pi.firstInstallTime = state.getFirstInstallTime();
             pi.lastUpdateTime = ps.getLastUpdateTime();
 
@@ -1627,7 +1622,7 @@
             long flags, int filterCallingUid, int userId) {
         // reader
         // Normalize package name to handle renamed packages and static libs
-        packageName = resolveInternalPackageNameLPr(packageName, versionCode);
+        packageName = resolveInternalPackageName(packageName, versionCode);
 
         final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
         if (matchFactoryOnly) {
@@ -2111,7 +2106,7 @@
         return packageName;
     }
 
-    public final String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+    public final String resolveInternalPackageName(String packageName, long versionCode) {
         final int callingUid = Binder.getCallingUid();
         return resolveInternalPackageNameInternalLocked(packageName, versionCode,
                 callingUid);
@@ -3489,7 +3484,9 @@
         return mSettings.getRenamedPackageLPr(packageName);
     }
 
-    private WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+    @NonNull
+    @Override
+    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
             getSharedLibraries() {
         return mSharedLibraries.getAll();
     }
@@ -4070,10 +4067,13 @@
     }
 
     @Override
-    public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
+    public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
             @UserIdInt int userId) {
-        final PackageStateInternal ps = getPackageStateInternal(packageName);
-        return ps != null && !shouldFilterApplication(ps, callingUid, userId);
+        final PackageStateInternal packageState = getPackageStateInternal(packageName);
+        if (packageState == null || shouldFilterApplication(packageState, callingUid, userId)) {
+            return null;
+        }
+        return packageState;
     }
 
     @Override
@@ -4576,9 +4576,9 @@
             }
         } else {
             list = new ArrayList<>(mPackages.size());
-            for (AndroidPackage p : mPackages.values()) {
-                final PackageStateInternal packageState = packageStates.get(p.getPackageName());
-                if (packageState == null) {
+            for (PackageStateInternal packageState : packageStates.values()) {
+                final AndroidPackage pkg = packageState.getPkg();
+                if (pkg == null) {
                     continue;
                 }
                 if (filterSharedLibPackage(packageState, Binder.getCallingUid(), userId, flags)) {
@@ -4587,10 +4587,10 @@
                 if (shouldFilterApplication(packageState, callingUid, userId)) {
                     continue;
                 }
-                ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
+                ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(pkg, flags,
                         packageState.getUserStateOrDefault(userId), userId, packageState);
                 if (ai != null) {
-                    ai.packageName = resolveExternalPackageName(p);
+                    ai.packageName = resolveExternalPackageName(pkg);
                     list.add(ai);
                 }
             }
@@ -5421,13 +5421,14 @@
             return EmptyArray.STRING;
         }
 
-        ArraySet<PackageSetting> packages = packageSetting.getSharedUser().packages;
+        ArraySet<? extends PackageStateInternal> packages =
+                packageSetting.getSharedUser().getPackageStates();
         final int numPackages = packages.size();
         String[] res = new String[numPackages];
         int i = 0;
         for (int index = 0; index < numPackages; index++) {
-            final PackageSetting ps = packages.valueAt(index);
-            if (ps.getInstalled(userId)) {
+            final PackageStateInternal ps = packages.valueAt(index);
+            if (ps.getUserStateOrDefault(userId).isInstalled()) {
                 res[i++] = ps.getPackageName();
             }
         }
@@ -5476,7 +5477,11 @@
                     + SET_HARMFUL_APP_WARNINGS + " permission.");
         }
 
-        return mSettings.getHarmfulAppWarning(packageName, userId);
+        final PackageStateInternal packageState = getPackageStateInternal(packageName);
+        if (packageState == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+        return packageState.getUserStateOrDefault(userId).getHarmfulAppWarning();
     }
 
     /**
@@ -5571,4 +5576,22 @@
         }
         return null;
     }
+
+    @Override
+    public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+        return mSettings.getBlockUninstall(userId, packageName);
+    }
+
+    @Nullable
+    @Override
+    public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+        final SettingBase settingBase = mSettings.getSettingBase(appId);
+        if (settingBase instanceof SharedUserSetting) {
+            return Pair.create(null, (SharedUserApi) settingBase);
+        } else if (settingBase instanceof PackageSetting) {
+            return Pair.create((PackageStateInternal) settingBase, null);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 529aca3..40d4c03 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -40,12 +40,16 @@
 import android.content.pm.VersionedPackage;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -390,14 +394,6 @@
     }
 
     @Override
-    public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
-            @UserIdInt int userId) {
-        synchronized (mLock) {
-            return super.isPackageStateAvailableAndVisible(packageName, callingUid, userId);
-        }
-    }
-
-    @Override
     public int checkSignatures(@NonNull String pkg1,
             @NonNull String pkg2) {
         synchronized (mLock) {
@@ -825,4 +821,35 @@
             return super.getProcessesForUid(uid);
         }
     }
+
+    @Override
+    public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.getPackageStateFiltered(packageName, callingUid, userId);
+        }
+    }
+
+    @Override
+    public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            return super.getBlockUninstall(userId, packageName);
+        }
+    }
+
+    @NonNull
+    @Override
+    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+        synchronized (mLock) {
+            return super.getSharedLibraries();
+        }
+    }
+
+    @Nullable
+    @Override
+    public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+        synchronized (mLock) {
+            return super.getPackageOrSharedUser(appId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 52309ce..24c08d1 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -42,11 +42,15 @@
 import android.content.pm.VersionedPackage;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -63,26 +67,11 @@
     // a snapshot computer.
     private final AtomicInteger mReusedSnapshot = new AtomicInteger(0);
 
-    // The number of times a thread reused a computer in its stack instead of fetching
-    // a live computer.
-    private final AtomicInteger mReusedLive = new AtomicInteger(0);
-
     private final PackageManagerService mService;
     ComputerTracker(PackageManagerService s) {
         mService = s;
     }
 
-    private ThreadComputer live() {
-        ThreadComputer current = PackageManagerService.sThreadComputer.get();
-        if (current.mRefCount > 0) {
-            current.acquire();
-            mReusedLive.incrementAndGet();
-        } else {
-            current.acquire(mService.liveComputer());
-        }
-        return current;
-    }
-
     private ThreadComputer snapshot() {
         ThreadComputer current = PackageManagerService.sThreadComputer.get();
         if (current.mRefCount > 0) {
@@ -133,7 +122,7 @@
             Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
             String pkgName, String instantAppPkgName) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.queryIntentActivitiesInternalBody(intent, resolvedType,
                     flags, filterCallingUid, userId, resolveForStart, allowDynamicSplits,
@@ -154,7 +143,7 @@
     public ActivityInfo getActivityInfoInternal(ComponentName component,
             @PackageManager.ComponentInfoFlagsBits long flags,
             int filterCallingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getActivityInfoInternal(component, flags, filterCallingUid,
                     userId);
@@ -180,7 +169,7 @@
     }
     public ApplicationInfo generateApplicationInfoFromSettings(String packageName,
             long flags, int filterCallingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.generateApplicationInfoFromSettings(packageName, flags,
                     filterCallingUid, userId);
@@ -199,7 +188,7 @@
     }
     public ApplicationInfo getApplicationInfoInternal(String packageName,
             @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getApplicationInfoInternal(packageName, flags,
                     filterCallingUid, userId);
@@ -208,7 +197,7 @@
         }
     }
     public ComponentName getDefaultHomeActivity(int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getDefaultHomeActivity(userId);
         } finally {
@@ -217,7 +206,7 @@
     }
     public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
             int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getHomeActivitiesAsUser(allHomeCandidates, userId);
         } finally {
@@ -227,7 +216,7 @@
     public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
             int parentUserId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getCrossProfileDomainPreferredLpr(intent, resolvedType,
                     flags, sourceUserId, parentUserId);
@@ -236,7 +225,7 @@
         }
     }
     public Intent getHomeIntent() {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getHomeIntent();
         } finally {
@@ -245,7 +234,7 @@
     }
     public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
             Intent intent, String resolvedType, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getMatchingCrossProfileIntentFilters(intent, resolvedType,
                     userId);
@@ -257,7 +246,7 @@
             @NonNull List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
             boolean resolveForStart, int userId, Intent intent) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.applyPostResolutionFilter(resolveInfos, ephemeralPkgName,
                     allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent);
@@ -267,7 +256,7 @@
     }
     public PackageInfo generatePackageInfo(PackageStateInternal ps,
             @PackageManager.PackageInfoFlagsBits long flags, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.generatePackageInfo(ps, flags, userId);
         } finally {
@@ -285,7 +274,7 @@
     }
     public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
             long flags, int filterCallingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getPackageInfoInternal(packageName, versionCode, flags,
                     filterCallingUid, userId);
@@ -302,7 +291,7 @@
         }
     }
     public PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getPackageStateInternal(packageName, callingUid);
         } finally {
@@ -312,7 +301,7 @@
 
     @Nullable
     public PackageState getPackageStateCopied(@NonNull String packageName) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getPackageStateCopied(packageName);
         } finally {
@@ -330,7 +319,7 @@
     }
     public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
             int sourceUserId, int targetUserId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.createForwardingResolveInfoUnchecked(filter, sourceUserId,
                     targetUserId);
@@ -340,7 +329,7 @@
     }
     public ServiceInfo getServiceInfo(ComponentName component,
             @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getServiceInfo(component, flags, userId);
         } finally {
@@ -380,17 +369,17 @@
         }
     }
     public String resolveExternalPackageName(AndroidPackage pkg) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.resolveExternalPackageName(pkg);
         } finally {
             current.release();
         }
     }
-    public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
-        ThreadComputer current = live();
+    public String resolveInternalPackageName(String packageName, long versionCode) {
+        ThreadComputer current = snapshot();
         try {
-            return current.mComputer.resolveInternalPackageNameLPr(packageName, versionCode);
+            return current.mComputer.resolveInternalPackageName(packageName, versionCode);
         } finally {
             current.release();
         }
@@ -404,7 +393,7 @@
         }
     }
     public UserInfo getProfileParent(int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getProfileParent(userId);
         } finally {
@@ -412,7 +401,7 @@
         }
     }
     public boolean canViewInstantApps(int callingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.canViewInstantApps(callingUid, userId);
         } finally {
@@ -445,7 +434,7 @@
     }
     public boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
             int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.filterSharedLibPackage(ps, uid, userId, flags);
         } finally {
@@ -453,7 +442,7 @@
         }
     }
     public boolean isCallerSameApp(String packageName, int uid) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isCallerSameApp(packageName, uid);
         } finally {
@@ -461,7 +450,7 @@
         }
     }
     public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isComponentVisibleToInstantApp(component);
         } finally {
@@ -470,7 +459,7 @@
     }
     public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
             @PackageManager.ComponentType int type) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isComponentVisibleToInstantApp(component, type);
         } finally {
@@ -479,7 +468,7 @@
     }
     public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
             int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent,
                     userId, resolvedType, flags);
@@ -497,7 +486,7 @@
     }
     public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
             int callingUid) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isInstantAppInternal(packageName, userId, callingUid);
         } finally {
@@ -506,7 +495,7 @@
     }
     public boolean isSameProfileGroup(@UserIdInt int callerUserId,
             @UserIdInt int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.isSameProfileGroup(callerUserId, userId);
         } finally {
@@ -515,7 +504,7 @@
     }
     public boolean shouldFilterApplication(@NonNull SharedUserSetting sus,
             int callingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.shouldFilterApplication(sus, callingUid, userId);
         } finally {
@@ -525,7 +514,7 @@
     public boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
             int callingUid, @Nullable ComponentName component,
             @PackageManager.ComponentType int componentType, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.shouldFilterApplication(ps, callingUid, component,
                     componentType, userId);
@@ -535,7 +524,7 @@
     }
     public boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
             int callingUid, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.shouldFilterApplication(ps, callingUid, userId);
         } finally {
@@ -552,7 +541,7 @@
     }
     public int getPackageUidInternal(String packageName,
             @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.getPackageUidInternal(packageName, flags, userId,
                     callingUid);
@@ -561,7 +550,7 @@
         }
     }
     public long updateFlagsForApplication(long flags, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.updateFlagsForApplication(flags, userId);
         } finally {
@@ -569,7 +558,7 @@
         }
     }
     public long updateFlagsForComponent(long flags, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.updateFlagsForComponent(flags, userId);
         } finally {
@@ -577,7 +566,7 @@
         }
     }
     public long updateFlagsForPackage(long flags, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.updateFlagsForPackage(flags, userId);
         } finally {
@@ -597,7 +586,7 @@
     public long updateFlagsForResolve(long flags, int userId, int callingUid,
             boolean wantInstantApps, boolean onlyExposedExplicitly,
             boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.updateFlagsForResolve(flags, userId, callingUid,
                     wantInstantApps, onlyExposedExplicitly,
@@ -607,7 +596,7 @@
         }
     }
     public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             current.mComputer.dump(type, fd, pw, dumpState);
         } finally {
@@ -616,7 +605,7 @@
     }
     public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
             boolean requireFullPermission, boolean checkShell, String message) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             current.mComputer.enforceCrossUserOrProfilePermission(callingUid, userId,
                     requireFullPermission, checkShell, message);
@@ -626,7 +615,7 @@
     }
     public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
             boolean requireFullPermission, boolean checkShell, String message) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             current.mComputer.enforceCrossUserPermission(callingUid, userId,
                     requireFullPermission, checkShell, message);
@@ -637,7 +626,7 @@
     public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
             boolean requireFullPermission, boolean checkShell,
             boolean requirePermissionWhenSameUser, String message) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             current.mComputer.enforceCrossUserPermission(callingUid, userId,
                     requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
@@ -649,7 +638,7 @@
             Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean always, boolean removeMatches, boolean debug,
             int userId, boolean queryMayBeFiltered) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags,
                     query, always, removeMatches, debug, userId, queryMayBeFiltered);
@@ -660,7 +649,7 @@
     public ResolveInfo findPersistentPreferredActivityLP(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
             List<ResolveInfo> query, boolean debug, int userId) {
-        ThreadComputer current = live();
+        ThreadComputer current = snapshot();
         try {
             return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType,
                     flags, query, debug, userId);
@@ -834,15 +823,6 @@
     }
 
     @Override
-    public boolean isPackageStateAvailableAndVisible(@NonNull String packageName, int callingUid,
-            @UserIdInt int userId) {
-        try (ThreadComputer current = snapshot()) {
-            return current.mComputer.isPackageStateAvailableAndVisible(packageName, callingUid,
-                    userId);
-        }
-    }
-
-    @Override
     public int checkSignatures(@NonNull String pkg1,
             @NonNull String pkg2) {
         try (ThreadComputer current = snapshot()) {
@@ -1272,4 +1252,35 @@
             return current.mComputer.getProcessesForUid(uid);
         }
     }
+
+    @Override
+    public PackageStateInternal getPackageStateFiltered(@NonNull String packageName, int callingUid,
+            @UserIdInt int userId) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getPackageStateFiltered(packageName, callingUid, userId);
+        }
+    }
+
+    @Override
+    public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getBlockUninstall(userId, packageName);
+        }
+    }
+
+    @NonNull
+    @Override
+    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getSharedLibraries();
+        }
+    }
+
+    @Nullable
+    @Override
+    public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getPackageOrSharedUser(appId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 48689a8..bd36b472 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -62,6 +62,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageUserState;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -220,12 +221,12 @@
                 res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
                         deleteFlags | PackageManager.DELETE_CHATTY, info, true);
             }
+            if (res && pkg != null) {
+                mPm.mInstantAppRegistry.onPackageUninstalled(pkg, uninstalledPs,
+                        info.mRemovedUsers);
+            }
             synchronized (mPm.mLock) {
                 if (res) {
-                    if (pkg != null) {
-                        mPm.mInstantAppRegistry.onPackageUninstalledLPw(pkg, uninstalledPs,
-                                info.mRemovedUsers);
-                    }
                     mPm.updateSequenceNumberLP(uninstalledPs, info.mRemovedUsers);
                     mPm.updateInstantAppInstallerLocked(packageName);
                 }
@@ -421,9 +422,7 @@
             }
             if (clearPackageStateAndReturn) {
                 clearPackageStateForUserLIF(ps, userId, outInfo, flags);
-                synchronized (mPm.mLock) {
-                    mPm.scheduleWritePackageRestrictionsLocked(user);
-                }
+                mPm.scheduleWritePackageRestrictions(user);
                 return;
             }
         }
@@ -622,7 +621,6 @@
 
         final String packageName = versionedPackage.getPackageName();
         final long versionCode = versionedPackage.getLongVersionCode();
-        final String internalPackageName;
 
         try {
             if (mPm.mInjector.getLocalService(ActivityTaskManagerInternal.class)
@@ -636,10 +634,8 @@
             e.rethrowFromSystemServer();
         }
 
-        synchronized (mPm.mLock) {
-            // Normalize package name to handle renamed packages and static libs
-            internalPackageName = mPm.resolveInternalPackageNameLPr(packageName, versionCode);
-        }
+        // Normalize package name to handle renamed packages and static libs
+        final String internalPackageName = mPm.resolveInternalPackageName(packageName, versionCode);
 
         final int uid = Binder.getCallingUid();
         if (!isOrphaned(internalPackageName)
@@ -748,13 +744,8 @@
     }
 
     private boolean isOrphaned(String packageName) {
-        // reader
-        synchronized (mPm.mLock) {
-            if (!mPm.mPackages.containsKey(packageName)) {
-                return false;
-            }
-            return mPm.mSettings.isOrphaned(packageName);
-        }
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(packageName);
+        return packageState != null && packageState.getInstallSource().isOrphaned;
     }
 
     private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
@@ -894,7 +885,7 @@
         int installedForUsersCount = 0;
         synchronized (mPm.mLock) {
             // Normalize package name to handle renamed packages and static libs
-            final String internalPkgName = mPm.resolveInternalPackageNameLPr(packageName,
+            final String internalPkgName = mPm.resolveInternalPackageName(packageName,
                     versionCode);
             final PackageSetting ps = mPm.mSettings.getPackageLPr(internalPkgName);
             if (ps != null) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index dcad3ec..ba89916 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -71,10 +71,15 @@
     private final PackageManagerService mPm;
 
     public boolean isDexOptDialogShown() {
-        return mDexOptDialogShown;
+        synchronized (mLock) {
+            return mDexOptDialogShown;
+        }
     }
 
-    @GuardedBy("mPm.mLock")
+    // TODO: Is this lock really necessary?
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private boolean mDexOptDialogShown;
 
     DexOptHelper(PackageManagerService pm) {
@@ -191,7 +196,7 @@
                                     numberOfPackagesVisited, numberOfPackagesToDexopt), true);
                 } catch (RemoteException e) {
                 }
-                synchronized (mPm.mLock) {
+                synchronized (mLock) {
                     mDexOptDialogShown = true;
                 }
             }
@@ -287,13 +292,11 @@
 
     public ArraySet<String> getOptimizablePackages() {
         ArraySet<String> pkgs = new ArraySet<>();
-        synchronized (mPm.mLock) {
-            for (AndroidPackage p : mPm.mPackages.values()) {
-                if (mPm.mPackageDexOptimizer.canOptimizePackage(p)) {
-                    pkgs.add(p.getPackageName());
-                }
+        mPm.forEachPackageState(packageState -> {
+            if (mPm.mPackageDexOptimizer.canOptimizePackage(packageState.getPkg())) {
+                pkgs.add(packageState.getPackageName());
             }
-        }
+        });
         return pkgs;
     }
 
@@ -415,14 +418,10 @@
     public void forceDexOpt(String packageName) {
         PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
 
-        AndroidPackage pkg;
-        PackageSetting pkgSetting;
-        synchronized (mPm.mLock) {
-            pkg = mPm.mPackages.get(packageName);
-            pkgSetting = mPm.mSettings.getPackageLPr(packageName);
-            if (pkg == null || pkgSetting == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
+        final PackageStateInternal packageState = mPm.getPackageStateInternal(packageName);
+        final AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
+        if (packageState == null || pkg == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
         }
 
         synchronized (mPm.mInstallLock) {
@@ -430,7 +429,7 @@
 
             // Whoever is calling forceDexOpt wants a compiled package.
             // Don't use profiles since that may cause compilation to be skipped.
-            final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
+            final int res = performDexOptInternalWithDependenciesLI(pkg, packageState,
                     new DexoptOptions(packageName,
                             getDefaultCompilerFilter(),
                             DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 55d1293..5ab0c4c 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -39,6 +39,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.function.BiConsumer;
 
 /**
  * Dumps PackageManagerService internal states.
@@ -117,7 +118,7 @@
                 }
 
                 // Normalize package name to handle renamed packages and static libs
-                pkg = mPm.resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
+                pkg = mPm.resolveInternalPackageName(pkg, PackageManager.VERSION_CODE_HIGHEST);
 
                 pw.println(mPm.checkPermission(perm, pkg, user));
                 return;
@@ -369,33 +370,20 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+            mPm.mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+            mPm.mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+            mPm.mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+            mPm.mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
             mPm.dumpComputer(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
         }
 
@@ -405,23 +393,16 @@
             mPm.dumpComputer(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
             mPm.dumpComputer(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
-            synchronized (mPm.mLock) {
-                mPm.mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+            mPm.mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
-            }
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+            mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
         }
 
         if (!checkin
@@ -461,28 +442,28 @@
                 pw.println();
             }
             pw.println("Package Changes:");
-            synchronized (mPm.mLock) {
-                pw.print("  Sequence number="); pw.println(mPm.mChangedPackagesSequenceNumber);
-                final int numChangedPackages = mPm.mChangedPackages.size();
+            mPm.mChangedPackagesTracker.iterateAll((sequenceNumber, values) -> {
+                pw.print("  Sequence number="); pw.println(sequenceNumber);
+                final int numChangedPackages = values.size();
                 for (int i = 0; i < numChangedPackages; i++) {
-                    final SparseArray<String> changes = mPm.mChangedPackages.valueAt(i);
-                    pw.print("  User "); pw.print(mPm.mChangedPackages.keyAt(i)); pw.println(":");
+                    final SparseArray<String> changes = values.valueAt(i);
+                    pw.print("  User "); pw.print(values.keyAt(i)); pw.println(":");
                     final int numChanges = changes.size();
                     if (numChanges == 0) {
                         pw.print("    "); pw.println("No packages changed");
                     } else {
                         for (int j = 0; j < numChanges; j++) {
                             final String pkgName = changes.valueAt(j);
-                            final int sequenceNumber = changes.keyAt(j);
+                            final int userSequenceNumber = changes.keyAt(j);
                             pw.print("    ");
                             pw.print("seq=");
-                            pw.print(sequenceNumber);
+                            pw.print(userSequenceNumber);
                             pw.print(", package=");
                             pw.println(pkgName);
                         }
                     }
                 }
-            }
+            });
         }
 
         if (!checkin
@@ -537,9 +518,7 @@
         if (!checkin
                 && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
                 && packageName == null) {
-            synchronized (mPm.mLock) {
-                mPm.mComponentResolver.dumpServicePermissions(pw, dumpState);
-            }
+            mPm.mComponentResolver.dumpServicePermissions(pw, dumpState);
         }
 
         if (!checkin
@@ -558,9 +537,7 @@
                 if (dumpState.onTitlePrinted()) {
                     pw.println();
                 }
-                synchronized (mPm.mLock) {
-                    mPm.mSettings.dumpReadMessagesLPr(pw, dumpState);
-                }
+                mPm.mSettings.dumpReadMessages(pw, dumpState);
                 pw.println();
                 pw.println("Package warning messages:");
                 dumpCriticalInfo(pw, null);
diff --git a/services/core/java/com/android/server/pm/IncrementalProgressListener.java b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
index 018501d..fa11924 100644
--- a/services/core/java/com/android/server/pm/IncrementalProgressListener.java
+++ b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
@@ -18,6 +18,8 @@
 
 import android.content.pm.IPackageLoadingProgressCallback;
 
+import com.android.server.pm.pkg.PackageStateInternal;
+
 /**
  * Loading progress callback, used to listen for progress changes and update package setting
  */
@@ -31,25 +33,24 @@
 
     @Override
     public void onPackageLoadingProgressChanged(float progress) {
-        final PackageSetting ps;
-        synchronized (mPm.mLock) {
-            ps = mPm.mSettings.getPackageLPr(mPackageName);
-            if (ps == null) {
-                return;
-            }
+        PackageStateInternal packageState = mPm.getPackageStateInternal(mPackageName);
+        if (packageState == null) {
+            return;
+        }
 
-            boolean wasLoading = ps.isLoading();
-            // Due to asynchronous progress reporting, incomplete progress might be received
-            // after the app is migrated off incremental. Ignore such progress updates.
-            if (wasLoading) {
-                ps.setLoadingProgress(progress);
-                // Only report the state change when loading state changes from loading to not
-                if (!ps.isLoading()) {
-                    // Unregister progress listener
-                    mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(ps.getPathString());
-                    // Make sure the information is preserved
-                    mPm.scheduleWriteSettings();
-                }
+        boolean wasLoading = packageState.isLoading();
+        // Due to asynchronous progress reporting, incomplete progress might be received
+        // after the app is migrated off incremental. Ignore such progress updates.
+        if (wasLoading) {
+            mPm.commitPackageStateMutation(null, mPackageName,
+                    state -> state.setLoadingProgress(progress));
+            // Only report the state change when loading state changes from loading to not
+            if (Math.abs(1.0f - progress) < 0.00000001f) {
+                // Unregister progress listener
+                mPm.mIncrementalManager
+                        .unregisterLoadingProgressCallbacks(packageState.getPathString());
+                // Make sure the information is preserved
+                mPm.scheduleWriteSettings();
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b636c8e..50c26f4 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -347,7 +347,7 @@
         commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
                 (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
         if (pkgSetting.getInstantApp(userId)) {
-            mPm.mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
+            mPm.mInstantAppRegistry.addInstantApp(userId, pkgSetting.getAppId());
         }
 
         if (!IncrementalManager.isIncrementalPath(pkgSetting.getPathString())) {
@@ -2614,9 +2614,7 @@
                                     ? res.mRemovedInfo.mInstallerPackageName
                                     : null;
 
-            synchronized (mPm.mLock) {
-                mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
-            }
+            mPm.notifyInstantAppPackageInstalled(res.mPkg.getPackageName(), res.mNewUsers);
 
             // Determine the set of users who are adding this package for
             // the first time vs. those who are seeing an update.
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 1de239e..ea6e458 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -18,12 +18,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.SigningDetails;
 import android.graphics.Bitmap;
@@ -93,7 +94,7 @@
  * pruning installed instant apps and meta-data for uninstalled instant apps
  * when free space is needed.
  */
-class InstantAppRegistry implements Watchable, Snappable {
+public class InstantAppRegistry implements Watchable, Snappable {
     private static final boolean DEBUG = false;
 
     private static final String LOG_TAG = "InstantAppRegistry";
@@ -125,14 +126,17 @@
     private static final String ATTR_NAME = "name";
     private static final String ATTR_GRANTED = "granted";
 
-    private final PackageManagerService mService;
+    private final Context mContext;
     private final PermissionManagerServiceInternal mPermissionManager;
+    private final UserManagerInternal mUserManager;
+    private final DeletePackageHelper mDeletePackageHelper;
     private final CookiePersistence mCookiePersistence;
-    private final PackageManagerInternal mPmInternal;
+
+    private final Object mLock = new Object();
 
     /** State for uninstalled instant apps */
     @Watched
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
 
     /**
@@ -142,12 +146,12 @@
      * UserID -> TargetAppId -> InstantAppId
      */
     @Watched
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
 
     /** The set of all installed instant apps. UserID -> AppID */
     @Watched
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
 
     /**
@@ -159,6 +163,7 @@
      * Watchable machinery
      */
     private final WatchableImpl mWatchable = new WatchableImpl();
+
     public void registerObserver(@NonNull Watcher observer) {
         mWatchable.registerObserver(observer);
     }
@@ -196,12 +201,14 @@
             }};
     }
 
-    public InstantAppRegistry(PackageManagerService service,
-            PermissionManagerServiceInternal permissionManager,
-            PackageManagerInternal pmInternal) {
-        mService = service;
+    public InstantAppRegistry(@NonNull Context context,
+            @NonNull PermissionManagerServiceInternal permissionManager,
+            @NonNull UserManagerInternal userManager,
+            @NonNull DeletePackageHelper deletePackageHelper) {
+        mContext = context;
         mPermissionManager = permissionManager;
-        mPmInternal = pmInternal;
+        mUserManager = userManager;
+        mDeletePackageHelper = deletePackageHelper;
         mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
 
         mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>();
@@ -220,9 +227,10 @@
      * The copy constructor is used by PackageManagerService to construct a snapshot.
      */
     private InstantAppRegistry(InstantAppRegistry r) {
-        mService = r.mService;
+        mContext = r.mContext;
         mPermissionManager = r.mPermissionManager;
-        mPmInternal = r.mPmInternal;
+        mUserManager = r.mUserManager;
+        mDeletePackageHelper = r.mDeletePackageHelper;
         mCookiePersistence = null;
 
         mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>(
@@ -243,56 +251,44 @@
         return mSnapshot.snapshot();
     }
 
-    @GuardedBy("mService.mLock")
-    public byte[] getInstantAppCookieLPw(@NonNull String packageName,
-            @UserIdInt int userId) {
-        // Only installed packages can get their own cookie
-        AndroidPackage pkg = mService.mPackages.get(packageName);
-        if (pkg == null) {
+    public byte[] getInstantAppCookie(@NonNull AndroidPackage pkg, @UserIdInt int userId) {
+        synchronized (mLock) {
+            byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
+            if (pendingCookie != null) {
+                return pendingCookie;
+            }
+            File cookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
+            if (cookieFile != null && cookieFile.exists()) {
+                try {
+                    return IoUtils.readFileAsByteArray(cookieFile.toString());
+                } catch (IOException e) {
+                    Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
+                }
+            }
             return null;
         }
-
-        byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
-        if (pendingCookie != null) {
-            return pendingCookie;
-        }
-        File cookieFile = peekInstantCookieFile(packageName, userId);
-        if (cookieFile != null && cookieFile.exists()) {
-            try {
-                return IoUtils.readFileAsByteArray(cookieFile.toString());
-            } catch (IOException e) {
-                Slog.w(LOG_TAG, "Error reading cookie file: " + cookieFile);
-            }
-        }
-        return null;
     }
 
-    @GuardedBy("mService.mLock")
-    public boolean setInstantAppCookieLPw(@NonNull String packageName,
-            @Nullable byte[] cookie, @UserIdInt int userId) {
-        if (cookie != null && cookie.length > 0) {
-            final int maxCookieSize = mService.mContext.getPackageManager()
-                    .getInstantAppCookieMaxBytes();
-            if (cookie.length > maxCookieSize) {
-                Slog.e(LOG_TAG, "Instant app cookie for package " + packageName + " size "
-                        + cookie.length + " bytes while max size is " + maxCookieSize);
-                return false;
+    public boolean setInstantAppCookie(@NonNull AndroidPackage pkg,
+            @Nullable byte[] cookie, int instantAppCookieMaxBytes, @UserIdInt int userId) {
+        synchronized (mLock) {
+            if (cookie != null && cookie.length > 0) {
+                if (cookie.length > instantAppCookieMaxBytes) {
+                    Slog.e(LOG_TAG, "Instant app cookie for package " + pkg.getPackageName()
+                            + " size " + cookie.length + " bytes while max size is "
+                            + instantAppCookieMaxBytes);
+                    return false;
+                }
             }
-        }
 
-        // Only an installed package can set its own cookie
-        AndroidPackage pkg = mService.mPackages.get(packageName);
-        if (pkg == null) {
-            return false;
+            mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
+            return true;
         }
-
-        mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
-        return true;
     }
 
     private void persistInstantApplicationCookie(@Nullable byte[] cookie,
             @NonNull String packageName, @NonNull File cookieFile, @UserIdInt int userId) {
-        synchronized (mService.mLock) {
+        synchronized (mLock) {
             File appDir = getInstantApplicationDir(packageName, userId);
             if (!appDir.exists() && !appDir.mkdirs()) {
                 Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
@@ -315,55 +311,54 @@
         }
     }
 
-    public Bitmap getInstantAppIconLPw(@NonNull String packageName,
-                                       @UserIdInt int userId) {
-        File iconFile = new File(getInstantApplicationDir(packageName, userId),
-                INSTANT_APP_ICON_FILE);
-        if (iconFile.exists()) {
-            return BitmapFactory.decodeFile(iconFile.toString());
-        }
-        return null;
-    }
-
-    public String getInstantAppAndroidIdLPw(@NonNull String packageName,
-                                            @UserIdInt int userId) {
-        File idFile = new File(getInstantApplicationDir(packageName, userId),
-                INSTANT_APP_ANDROID_ID_FILE);
-        if (idFile.exists()) {
-            try {
-                return IoUtils.readFileAsString(idFile.getAbsolutePath());
-            } catch (IOException e) {
-                Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e);
+    @Nullable
+    public Bitmap getInstantAppIcon(@NonNull String packageName, @UserIdInt int userId) {
+        synchronized (mLock) {
+            File iconFile = new File(getInstantApplicationDir(packageName, userId),
+                    INSTANT_APP_ICON_FILE);
+            if (iconFile.exists()) {
+                return BitmapFactory.decodeFile(iconFile.toString());
             }
+            return null;
         }
-        return generateInstantAppAndroidIdLPw(packageName, userId);
     }
 
-    private String generateInstantAppAndroidIdLPw(@NonNull String packageName,
-                                                @UserIdInt int userId) {
-        byte[] randomBytes = new byte[8];
-        new SecureRandom().nextBytes(randomBytes);
-        String id = HexEncoding.encodeToString(randomBytes, false /* upperCase */);
-        File appDir = getInstantApplicationDir(packageName, userId);
-        if (!appDir.exists() && !appDir.mkdirs()) {
-            Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
+    @Nullable
+    public String getInstantAppAndroidId(@NonNull String packageName, @UserIdInt int userId) {
+        synchronized (mLock) {
+            File idFile = new File(getInstantApplicationDir(packageName, userId),
+                    INSTANT_APP_ANDROID_ID_FILE);
+            if (idFile.exists()) {
+                try {
+                    return IoUtils.readFileAsString(idFile.getAbsolutePath());
+                } catch (IOException e) {
+                    Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e);
+                }
+            }
+
+            byte[] randomBytes = new byte[8];
+            new SecureRandom().nextBytes(randomBytes);
+            String id = HexEncoding.encodeToString(randomBytes, false /* upperCase */);
+            File appDir = getInstantApplicationDir(packageName, userId);
+            if (!appDir.exists() && !appDir.mkdirs()) {
+                Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
+                return id;
+            }
+            idFile = new File(getInstantApplicationDir(packageName, userId),
+                    INSTANT_APP_ANDROID_ID_FILE);
+            try (FileOutputStream fos = new FileOutputStream(idFile)) {
+                fos.write(id.getBytes());
+            } catch (IOException e) {
+                Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e);
+            }
             return id;
         }
-        File idFile = new File(getInstantApplicationDir(packageName, userId),
-                INSTANT_APP_ANDROID_ID_FILE);
-        try (FileOutputStream fos = new FileOutputStream(idFile)) {
-            fos.write(id.getBytes());
-        } catch (IOException e) {
-            Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e);
-        }
-        return id;
-
     }
 
-    @GuardedBy("mService.mLock")
-    public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
-        List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
-        List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
+    @Nullable
+    public List<InstantAppInfo> getInstantApps(@NonNull Computer computer, @UserIdInt int userId) {
+        List<InstantAppInfo> installedApps = getInstalledInstantApplications(computer, userId);
+        List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplications(computer, userId);
         if (installedApps != null) {
             if (uninstalledApps != null) {
                 installedApps.addAll(uninstalledApps);
@@ -373,123 +368,130 @@
         return uninstalledApps;
     }
 
-    @GuardedBy("mService.mLock")
-    public void onPackageInstalledLPw(@NonNull AndroidPackage pkg, @NonNull int[] userIds) {
-        PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
-        if (ps == null) {
+    public void onPackageInstalled(@NonNull Computer computer, @NonNull String packageName,
+            @NonNull int[] userIds) {
+        PackageStateInternal ps = computer.getPackageStateInternal(packageName);
+        AndroidPackage pkg = ps == null ? null : ps.getPkg();
+        if (pkg == null) {
             return;
         }
 
-        for (int userId : userIds) {
-            // Ignore not installed apps
-            if (mService.mPackages.get(pkg.getPackageName()) == null
-                    || !ps.getUserStateOrDefault(userId).isInstalled()) {
-                continue;
-            }
+        synchronized (mLock) {
+            for (int userId : userIds) {
+                // Ignore not installed apps
+                if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+                    continue;
+                }
 
-            // Propagate permissions before removing any state
-            propagateInstantAppPermissionsIfNeeded(pkg, userId);
+                // Propagate permissions before removing any state
+                propagateInstantAppPermissionsIfNeeded(pkg, userId);
 
-            // Track instant apps
-            if (ps.getUserStateOrDefault(userId).isInstantApp()) {
-                addInstantAppLPw(userId, ps.getAppId());
-            }
+                // Track instant apps
+                if (ps.getUserStateOrDefault(userId).isInstantApp()) {
+                    addInstantApp(userId, ps.getAppId());
+                }
 
-            // Remove the in-memory state
-            removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
-                            state.mInstantAppInfo.getPackageName().equals(pkg.getPackageName()),
-                    userId);
+                // Remove the in-memory state
+                removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+                                state.mInstantAppInfo.getPackageName().equals(pkg.getPackageName()),
+                        userId);
 
-            // Remove the on-disk state except the cookie
-            File instantAppDir = getInstantApplicationDir(pkg.getPackageName(), userId);
-            new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
-            new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+                // Remove the on-disk state except the cookie
+                File instantAppDir = getInstantApplicationDir(pkg.getPackageName(), userId);
+                new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+                new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
 
-            // If app signature changed - wipe the cookie
-            File currentCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
-            if (currentCookieFile == null) {
-                continue;
-            }
+                // If app signature changed - wipe the cookie
+                File currentCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
+                if (currentCookieFile == null) {
+                    continue;
+                }
 
-            String cookieName = currentCookieFile.getName();
-            String currentCookieSha256 =
-                    cookieName.substring(INSTANT_APP_COOKIE_FILE_PREFIX.length(),
-                            cookieName.length() - INSTANT_APP_COOKIE_FILE_SIFFIX.length());
+                String cookieName = currentCookieFile.getName();
+                String currentCookieSha256 =
+                        cookieName.substring(INSTANT_APP_COOKIE_FILE_PREFIX.length(),
+                                cookieName.length() - INSTANT_APP_COOKIE_FILE_SIFFIX.length());
 
-            // Before we used only the first signature to compute the SHA 256 but some
-            // apps could be singed by multiple certs and the cert order is undefined.
-            // We prefer the modern computation procedure where all certs are taken
-            // into account but also allow the value from the old computation to avoid
-            // data loss.
-            if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
-                    SigningDetails.CertCapabilities.INSTALLED_DATA)) {
-                return;
-            }
-
-            // For backwards compatibility we accept match based on any signature, since we may have
-            // recorded only the first for multiply-signed packages
-            final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
-                    pkg.getSigningDetails().getSignatures());
-            for (String s : signaturesSha256Digests) {
-                if (s.equals(currentCookieSha256)) {
+                // Before we used only the first signature to compute the SHA 256 but some
+                // apps could be singed by multiple certs and the cert order is undefined.
+                // We prefer the modern computation procedure where all certs are taken
+                // into account but also allow the value from the old computation to avoid
+                // data loss.
+                if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
+                        SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                     return;
                 }
-            }
 
-            // Sorry, you are out of luck - different signatures - nuke data
-            Slog.i(LOG_TAG, "Signature for package " + pkg.getPackageName()
-                    + " changed - dropping cookie");
+                // For backwards compatibility we accept match based on any signature, since we may
+                // have recorded only the first for multiply-signed packages
+                final String[] signaturesSha256Digests =
+                        PackageUtils.computeSignaturesSha256Digests(
+                                pkg.getSigningDetails().getSignatures());
+                for (String s : signaturesSha256Digests) {
+                    if (s.equals(currentCookieSha256)) {
+                        return;
+                    }
+                }
+
+                // Sorry, you are out of luck - different signatures - nuke data
+                Slog.i(LOG_TAG, "Signature for package " + pkg.getPackageName()
+                        + " changed - dropping cookie");
                 // Make sure a pending write for the old signed app is cancelled
-            mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
-            currentCookieFile.delete();
+                mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
+                currentCookieFile.delete();
+            }
         }
     }
 
-    @GuardedBy("mService.mLock")
-    public void onPackageUninstalledLPw(@NonNull AndroidPackage pkg, @Nullable PackageSetting ps,
+    public void onPackageUninstalled(@NonNull AndroidPackage pkg, @NonNull PackageSetting ps,
             @NonNull int[] userIds) {
         if (ps == null) {
             return;
         }
 
-        for (int userId : userIds) {
-            if (mService.mPackages.get(pkg.getPackageName()) != null && ps.getInstalled(userId)) {
-                continue;
-            }
+        synchronized (mLock) {
+            for (int userId : userIds) {
+                if (ps.getInstalled(userId)) {
+                    continue;
+                }
 
-            if (ps.getInstantApp(userId)) {
-                // Add a record for an uninstalled instant app
-                addUninstalledInstantAppLPw(pkg, userId);
-                removeInstantAppLPw(userId, ps.getAppId());
-            } else {
-                // Deleting an app prunes all instant state such as cookie
-                deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
-                mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
-                removeAppLPw(userId, ps.getAppId());
+                if (ps.getInstantApp(userId)) {
+                    // Add a record for an uninstalled instant app
+                    addUninstalledInstantAppLPw(ps, userId);
+                    removeInstantAppLPw(userId, ps.getAppId());
+                } else {
+                    // Deleting an app prunes all instant state such as cookie
+                    deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
+                    mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
+                    removeAppLPw(userId, ps.getAppId());
+                }
             }
         }
     }
 
-    @GuardedBy("mService.mLock")
-    public void onUserRemovedLPw(int userId) {
-        mUninstalledInstantApps.remove(userId);
-        mInstalledInstantAppUids.remove(userId);
-        mInstantGrants.remove(userId);
-        deleteDir(getInstantApplicationsDir(userId));
+    public void onUserRemoved(int userId) {
+        synchronized (mLock) {
+            mUninstalledInstantApps.remove(userId);
+            mInstalledInstantAppUids.remove(userId);
+            mInstantGrants.remove(userId);
+            deleteDir(getInstantApplicationsDir(userId));
+        }
     }
 
     public boolean isInstantAccessGranted(@UserIdInt int userId, int targetAppId,
             int instantAppId) {
-        final WatchedSparseArray<WatchedSparseBooleanArray> targetAppList =
-                mInstantGrants.get(userId);
-        if (targetAppList == null) {
-            return false;
+        synchronized (mLock) {
+            final WatchedSparseArray<WatchedSparseBooleanArray> targetAppList =
+                    mInstantGrants.get(userId);
+            if (targetAppList == null) {
+                return false;
+            }
+            final WatchedSparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+            if (instantGrantList == null) {
+                return false;
+            }
+            return instantGrantList.get(instantAppId);
         }
-        final WatchedSparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
-        if (instantGrantList == null) {
-            return false;
-        }
-        return instantGrantList.get(instantAppId);
     }
 
     /**
@@ -503,51 +505,54 @@
      *                      to the recipient
      * @return {@code true} if access is granted.
      */
-    @GuardedBy("mService.mLock")
-    public boolean grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
+    public boolean grantInstantAccess(@UserIdInt int userId, @Nullable Intent intent,
             int recipientUid, int instantAppId) {
-        if (mInstalledInstantAppUids == null) {
-            return false;     // no instant apps installed; no need to grant
-        }
-        WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
-        if (instantAppList == null || !instantAppList.get(instantAppId)) {
-            return false;     // instant app id isn't installed; no need to grant
-        }
-        if (instantAppList.get(recipientUid)) {
-            return false;     // target app id is an instant app; no need to grant
-        }
-        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
-            final Set<String> categories = intent.getCategories();
-            if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
-                return false;  // launched via VIEW/BROWSABLE intent; no need to grant
+        synchronized (mLock) {
+            if (mInstalledInstantAppUids == null) {
+                return false;     // no instant apps installed; no need to grant
             }
+            WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+            if (instantAppList == null || !instantAppList.get(instantAppId)) {
+                return false;     // instant app id isn't installed; no need to grant
+            }
+            if (instantAppList.get(recipientUid)) {
+                return false;     // target app id is an instant app; no need to grant
+            }
+            if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+                final Set<String> categories = intent.getCategories();
+                if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+                    return false;  // launched via VIEW/BROWSABLE intent; no need to grant
+                }
+            }
+            WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(
+                    userId);
+            if (targetAppList == null) {
+                targetAppList = new WatchedSparseArray<>();
+                mInstantGrants.put(userId, targetAppList);
+            }
+            WatchedSparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
+            if (instantGrantList == null) {
+                instantGrantList = new WatchedSparseBooleanArray();
+                targetAppList.put(recipientUid, instantGrantList);
+            }
+            instantGrantList.put(instantAppId, true /*granted*/);
+            return true;
         }
-        WatchedSparseArray<WatchedSparseBooleanArray> targetAppList = mInstantGrants.get(userId);
-        if (targetAppList == null) {
-            targetAppList = new WatchedSparseArray<>();
-            mInstantGrants.put(userId, targetAppList);
-        }
-        WatchedSparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
-        if (instantGrantList == null) {
-            instantGrantList = new WatchedSparseBooleanArray();
-            targetAppList.put(recipientUid, instantGrantList);
-        }
-        instantGrantList.put(instantAppId, true /*granted*/);
-        return true;
     }
 
-    @GuardedBy("mService.mLock")
-    public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) {
-        WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
-        if (instantAppList == null) {
-            instantAppList = new WatchedSparseBooleanArray();
-            mInstalledInstantAppUids.put(userId, instantAppList);
+    public void addInstantApp(@UserIdInt int userId, int instantAppId) {
+        synchronized (mLock) {
+            WatchedSparseBooleanArray instantAppList = mInstalledInstantAppUids.get(userId);
+            if (instantAppList == null) {
+                instantAppList = new WatchedSparseBooleanArray();
+                mInstalledInstantAppUids.put(userId, instantAppList);
+            }
+            instantAppList.put(instantAppId, true /*installed*/);
         }
-        instantAppList.put(instantAppId, true /*installed*/);
         onChanged();
     }
 
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) {
         // remove from the installed list
         if (mInstalledInstantAppUids == null) {
@@ -578,7 +583,7 @@
         }
     }
 
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private void removeAppLPw(@UserIdInt int userId, int targetAppId) {
         // remove from the installed list
         if (mInstantGrants == null) {
@@ -593,11 +598,11 @@
         onChanged();
     }
 
-    @GuardedBy("mService.mLock")
-    private void addUninstalledInstantAppLPw(@NonNull AndroidPackage pkg,
+    @GuardedBy("mLock")
+    private void addUninstalledInstantAppLPw(@NonNull PackageStateInternal packageState,
             @UserIdInt int userId) {
         InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
-                pkg, userId, false);
+                packageState, userId, false);
         if (uninstalledApp == null) {
             return;
         }
@@ -612,7 +617,7 @@
         uninstalledAppStates.add(uninstalledAppState);
 
         writeUninstalledInstantAppMetadata(uninstalledApp, userId);
-        writeInstantApplicationIconLPw(pkg, userId);
+        writeInstantApplicationIconLPw(packageState.getPkg(), userId);
     }
 
     private void writeInstantApplicationIconLPw(@NonNull AndroidPackage pkg,
@@ -624,7 +629,7 @@
 
         // TODO(b/135203078): Remove toAppInfo call? Requires significant additions/changes to PM
         Drawable icon = AndroidPackageUtils.generateAppInfoWithoutState(pkg)
-                .loadIcon(mService.mContext.getPackageManager());
+                .loadIcon(mContext.getPackageManager());
 
         final Bitmap bitmap;
         if (icon instanceof BitmapDrawable) {
@@ -647,30 +652,30 @@
         }
     }
 
-    @GuardedBy("mService.mLock")
-    boolean hasInstantApplicationMetadataLPr(String packageName, int userId) {
-        return hasUninstalledInstantAppStateLPr(packageName, userId)
-                || hasInstantAppMetadataLPr(packageName, userId);
+    boolean hasInstantApplicationMetadata(String packageName, int userId) {
+        return hasUninstalledInstantAppState(packageName, userId)
+                || hasInstantAppMetadata(packageName, userId);
     }
 
-    @GuardedBy("mService.mLock")
-    public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
+    public void deleteInstantApplicationMetadata(@NonNull String packageName,
             @UserIdInt int userId) {
-        removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
-                state.mInstantAppInfo.getPackageName().equals(packageName),
-                userId);
+        synchronized (mLock) {
+            removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
+                            state.mInstantAppInfo.getPackageName().equals(packageName),
+                    userId);
 
-        File instantAppDir = getInstantApplicationDir(packageName, userId);
-        new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
-        new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
-        new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete();
-        File cookie = peekInstantCookieFile(packageName, userId);
-        if (cookie != null) {
-            cookie.delete();
+            File instantAppDir = getInstantApplicationDir(packageName, userId);
+            new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
+            new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+            new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete();
+            File cookie = peekInstantCookieFile(packageName, userId);
+            if (cookie != null) {
+                cookie.delete();
+            }
         }
     }
 
-    @GuardedBy("mService.mLock")
+    @GuardedBy("mLock")
     private void removeUninstalledInstantAppStateLPw(
             @NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) {
         if (mUninstalledInstantApps == null) {
@@ -696,27 +701,28 @@
         }
     }
 
-    @GuardedBy("mService.mLock")
-    private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) {
-        if (mUninstalledInstantApps == null) {
-            return false;
-        }
-        final List<UninstalledInstantAppState> uninstalledAppStates =
-                mUninstalledInstantApps.get(userId);
-        if (uninstalledAppStates == null) {
-            return false;
-        }
-        final int appCount = uninstalledAppStates.size();
-        for (int i = 0; i < appCount; i++) {
-            final UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
-            if (packageName.equals(uninstalledAppState.mInstantAppInfo.getPackageName())) {
-                return true;
+    private boolean hasUninstalledInstantAppState(String packageName, @UserIdInt int userId) {
+        synchronized (mLock) {
+            if (mUninstalledInstantApps == null) {
+                return false;
             }
+            final List<UninstalledInstantAppState> uninstalledAppStates =
+                    mUninstalledInstantApps.get(userId);
+            if (uninstalledAppStates == null) {
+                return false;
+            }
+            final int appCount = uninstalledAppStates.size();
+            for (int i = 0; i < appCount; i++) {
+                final UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
+                if (packageName.equals(uninstalledAppState.mInstantAppInfo.getPackageName())) {
+                    return true;
+                }
+            }
+            return false;
         }
-        return false;
     }
 
-    private boolean hasInstantAppMetadataLPr(String packageName, @UserIdInt int userId) {
+    private boolean hasInstantAppMetadata(String packageName, @UserIdInt int userId) {
         final File instantAppDir = getInstantApplicationDir(packageName, userId);
         return new File(instantAppDir, INSTANT_APP_METADATA_FILE).exists()
                 || new File(instantAppDir, INSTANT_APP_ICON_FILE).exists()
@@ -724,37 +730,41 @@
                 || peekInstantCookieFile(packageName, userId) != null;
     }
 
-    void pruneInstantApps() {
+    void pruneInstantApps(@NonNull Computer computer) {
         final long maxInstalledCacheDuration = Settings.Global.getLong(
-                mService.mContext.getContentResolver(),
+                mContext.getContentResolver(),
                 Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                 DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
 
         final long maxUninstalledCacheDuration = Settings.Global.getLong(
-                mService.mContext.getContentResolver(),
+                mContext.getContentResolver(),
                 Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                 DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
 
         try {
-            pruneInstantApps(Long.MAX_VALUE,
+            pruneInstantApps(computer, Long.MAX_VALUE,
                     maxInstalledCacheDuration, maxUninstalledCacheDuration);
         } catch (IOException e) {
             Slog.e(LOG_TAG, "Error pruning installed and uninstalled instant apps", e);
         }
     }
 
-    boolean pruneInstalledInstantApps(long neededSpace, long maxInstalledCacheDuration) {
+    boolean pruneInstalledInstantApps(@NonNull Computer computer, long neededSpace,
+            long maxInstalledCacheDuration) {
         try {
-            return pruneInstantApps(neededSpace, maxInstalledCacheDuration, Long.MAX_VALUE);
+            return pruneInstantApps(computer, neededSpace, maxInstalledCacheDuration,
+                    Long.MAX_VALUE);
         } catch (IOException e) {
             Slog.e(LOG_TAG, "Error pruning installed instant apps", e);
             return false;
         }
     }
 
-    boolean pruneUninstalledInstantApps(long neededSpace, long maxUninstalledCacheDuration) {
+    boolean pruneUninstalledInstantApps(@NonNull Computer computer, long neededSpace,
+            long maxUninstalledCacheDuration) {
         try {
-            return pruneInstantApps(neededSpace, Long.MAX_VALUE, maxUninstalledCacheDuration);
+            return pruneInstantApps(computer, neededSpace, Long.MAX_VALUE,
+                    maxUninstalledCacheDuration);
         } catch (IOException e) {
             Slog.e(LOG_TAG, "Error pruning uninstalled instant apps", e);
             return false;
@@ -774,9 +784,9 @@
      *
      * @throws IOException
      */
-    private boolean pruneInstantApps(long neededSpace, long maxInstalledCacheDuration,
-            long maxUninstalledCacheDuration) throws IOException {
-        final StorageManager storage = mService.mContext.getSystemService(StorageManager.class);
+    private boolean pruneInstantApps(@NonNull Computer computer, long neededSpace,
+            long maxInstalledCacheDuration, long maxUninstalledCacheDuration) throws IOException {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final File file = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
 
         if (file.getUsableSpace() >= neededSpace) {
@@ -789,105 +799,105 @@
         final long now = System.currentTimeMillis();
 
         // Prune first installed instant apps
-        synchronized (mService.mLock) {
-            allUsers = mService.mUserManager.getUserIds();
+        allUsers = mUserManager.getUserIds();
 
-            final int packageCount = mService.mPackages.size();
-            for (int i = 0; i < packageCount; i++) {
-                final AndroidPackage pkg = mService.mPackages.valueAt(i);
-                final PackageStateInternal ps =
-                        mPmInternal.getPackageStateInternal(pkg.getPackageName());
-                if (ps == null) {
-                    continue;
-                }
-
-                if (now - ps.getTransientState().getLatestPackageUseTimeInMills()
-                        < maxInstalledCacheDuration) {
-                    continue;
-                }
-
-                boolean installedOnlyAsInstantApp = false;
-                for (int userId : allUsers) {
-                    final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-                    if (userState.isInstalled()) {
-                        if (userState.isInstantApp()) {
-                            installedOnlyAsInstantApp = true;
-                        } else {
-                            installedOnlyAsInstantApp = false;
-                            break;
-                        }
-                    }
-                }
-                if (installedOnlyAsInstantApp) {
-                    if (packagesToDelete == null) {
-                        packagesToDelete = new ArrayList<>();
-                    }
-                    packagesToDelete.add(pkg.getPackageName());
-                }
+        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+                computer.getPackageStates();
+        final int packageStateCount = packageStates.size();
+        for (int i = 0; i < packageStateCount; i++) {
+            final PackageStateInternal ps = packageStates.valueAt(i);
+            final AndroidPackage pkg = ps == null ? null : ps.getPkg();
+            if (pkg == null) {
+                continue;
             }
 
-            if (packagesToDelete != null) {
-                packagesToDelete.sort((String lhs, String rhs) -> {
-                    final AndroidPackage lhsPkg = mService.mPackages.get(lhs);
-                    final AndroidPackage rhsPkg = mService.mPackages.get(rhs);
-                    if (lhsPkg == null && rhsPkg == null) {
-                        return 0;
-                    } else if (lhsPkg == null) {
-                        return -1;
-                    } else if (rhsPkg == null) {
-                        return 1;
+            if (now - ps.getTransientState().getLatestPackageUseTimeInMills()
+                    < maxInstalledCacheDuration) {
+                continue;
+            }
+
+            boolean installedOnlyAsInstantApp = false;
+            for (int userId : allUsers) {
+                final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+                if (userState.isInstalled()) {
+                    if (userState.isInstantApp()) {
+                        installedOnlyAsInstantApp = true;
                     } else {
-                        final PackageStateInternal lhsPs =
-                                mPmInternal.getPackageStateInternal(lhsPkg.getPackageName());
-                        if (lhsPs == null) {
-                            return 0;
-                        }
-
-                        final PackageStateInternal rhsPs =
-                                mPmInternal.getPackageStateInternal(rhsPkg.getPackageName());
-                        if (rhsPs == null) {
-                            return 0;
-                        }
-
-                        if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() >
-                                rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
-                            return 1;
-                        } else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
-                                rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
-                            return -1;
-                        } else if (
-                                PackageStateUtils.getEarliestFirstInstallTime(lhsPs.getUserStates())
-                                > PackageStateUtils.getEarliestFirstInstallTime(
-                                        rhsPs.getUserStates())) {
-                            return 1;
-                        } else {
-                            return -1;
-                        }
+                        installedOnlyAsInstantApp = false;
+                        break;
                     }
-                });
+                }
+            }
+            if (installedOnlyAsInstantApp) {
+                if (packagesToDelete == null) {
+                    packagesToDelete = new ArrayList<>();
+                }
+                packagesToDelete.add(pkg.getPackageName());
             }
         }
 
         if (packagesToDelete != null) {
-            final int packageCount = packagesToDelete.size();
-            final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mService);
-            for (int i = 0; i < packageCount; i++) {
-                final String packageToDelete = packagesToDelete.get(i);
-                if (deletePackageHelper.deletePackageX(packageToDelete,
-                        PackageManager.VERSION_CODE_HIGHEST,
-                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
-                        true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
-                    if (file.getUsableSpace() >= neededSpace) {
-                        return true;
+            packagesToDelete.sort((String lhs, String rhs) -> {
+                final PackageStateInternal lhsPkgState = packageStates.get(lhs);
+                final PackageStateInternal rhsPkgState = packageStates.get(rhs);
+                final AndroidPackage lhsPkg = lhsPkgState == null ? null : lhsPkgState.getPkg();
+                final AndroidPackage rhsPkg = rhsPkgState == null ? null : rhsPkgState.getPkg();
+                if (lhsPkg == null && rhsPkg == null) {
+                    return 0;
+                } else if (lhsPkg == null) {
+                    return -1;
+                } else if (rhsPkg == null) {
+                    return 1;
+                } else {
+                    final PackageStateInternal lhsPs =
+                            packageStates.get(lhsPkg.getPackageName());
+                    if (lhsPs == null) {
+                        return 0;
+                    }
+
+                    final PackageStateInternal rhsPs =
+                            packageStates.get(rhsPkg.getPackageName());
+                    if (rhsPs == null) {
+                        return 0;
+                    }
+
+                    if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() >
+                            rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
+                        return 1;
+                    } else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
+                            rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
+                        return -1;
+                    } else if (
+                            PackageStateUtils.getEarliestFirstInstallTime(lhsPs.getUserStates())
+                                    > PackageStateUtils.getEarliestFirstInstallTime(
+                                    rhsPs.getUserStates())) {
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                }
+            });
+        }
+
+        synchronized (mLock) {
+            if (packagesToDelete != null) {
+                final int packageCount = packagesToDelete.size();
+                for (int i = 0; i < packageCount; i++) {
+                    final String packageToDelete = packagesToDelete.get(i);
+                    if (mDeletePackageHelper.deletePackageX(packageToDelete,
+                            PackageManager.VERSION_CODE_HIGHEST,
+                            UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS,
+                            true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
+                        if (file.getUsableSpace() >= neededSpace) {
+                            return true;
+                        }
                     }
                 }
             }
-        }
 
-        // Prune uninstalled instant apps
-        synchronized (mService.mLock) {
+            // Prune uninstalled instant apps
             // TODO: Track last used time for uninstalled instant apps for better pruning
-            for (int userId : UserManagerService.getInstance().getUserIds()) {
+            for (int userId : mUserManager.getUserIds()) {
                 // Prune in-memory state
                 removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
                     final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
@@ -928,21 +938,19 @@
         return false;
     }
 
-    @GuardedBy("mService.mLock")
-    private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
-            @UserIdInt int userId) {
+    private @Nullable List<InstantAppInfo> getInstalledInstantApplications(
+            @NonNull Computer computer, @UserIdInt int userId) {
         List<InstantAppInfo> result = null;
 
-        final int packageCount = mService.mPackages.size();
+        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+                computer.getPackageStates();
+        final int packageCount = packageStates.size();
         for (int i = 0; i < packageCount; i++) {
-            final AndroidPackage pkg = mService.mPackages.valueAt(i);
-            final PackageStateInternal ps =
-                    mPmInternal.getPackageStateInternal(pkg.getPackageName());
+            final PackageStateInternal ps = packageStates.valueAt(i);
             if (ps == null || !ps.getUserStateOrDefault(userId).isInstantApp()) {
                 continue;
             }
-            final InstantAppInfo info = createInstantAppInfoForPackage(
-                    pkg, userId, true);
+            final InstantAppInfo info = createInstantAppInfoForPackage(ps, userId, true);
             if (info == null) {
                 continue;
             }
@@ -956,14 +964,10 @@
     }
 
     private @NonNull
-    InstantAppInfo createInstantAppInfoForPackage(
-            @NonNull AndroidPackage pkg, @UserIdInt int userId,
-            boolean addApplicationInfo) {
-        PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
-        if (ps == null) {
-            return null;
-        }
-        if (!ps.getUserStateOrDefault(userId).isInstalled()) {
+    InstantAppInfo createInstantAppInfoForPackage(@NonNull PackageStateInternal ps,
+            @UserIdInt int userId, boolean addApplicationInfo) {
+        AndroidPackage pkg = ps.getPkg();
+        if (pkg == null || !ps.getUserStateOrDefault(userId).isInstalled()) {
             return null;
         }
 
@@ -982,17 +986,18 @@
         if (addApplicationInfo) {
             return new InstantAppInfo(appInfo, requestedPermissions, grantedPermissions);
         } else {
+            // TODO: PMS lock re-entry
             return new InstantAppInfo(appInfo.packageName,
-                    appInfo.loadLabel(mService.mContext.getPackageManager()),
+                    appInfo.loadLabel(mContext.getPackageManager()),
                     requestedPermissions, grantedPermissions);
         }
     }
 
-    @GuardedBy("mService.mLock")
-    private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr(
+    @Nullable
+    private List<InstantAppInfo> getUninstalledInstantApplications(@NonNull Computer computer,
             @UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates =
-                getUninstalledInstantAppStatesLPr(userId);
+                getUninstalledInstantAppStates(userId);
         if (uninstalledAppStates == null || uninstalledAppStates.isEmpty()) {
             return null;
         }
@@ -1009,6 +1014,7 @@
         return uninstalledApps;
     }
 
+    @SuppressLint("MissingPermission")
     private void propagateInstantAppPermissionsIfNeeded(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
         InstantAppInfo appInfo = peekOrParseUninstalledInstantAppInfo(
@@ -1025,8 +1031,9 @@
                 final boolean propagatePermission = canPropagatePermission(grantedPermission);
                 if (propagatePermission && pkg.getRequestedPermissions().contains(
                         grantedPermission)) {
-                    mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission,
-                            userId);
+                    mContext.getSystemService(PermissionManager.class)
+                            .grantRuntimePermission(pkg.getPackageName(), grantedPermission,
+                                    UserHandle.of(userId));
                 }
             }
         } finally {
@@ -1035,8 +1042,8 @@
     }
 
     private boolean canPropagatePermission(@NonNull String permissionName) {
-        final PermissionManager permissionManager = mService.mContext.getSystemService(
-                PermissionManager.class);
+        final PermissionManager permissionManager =
+                mContext.getSystemService(PermissionManager.class);
         final PermissionInfo permissionInfo = permissionManager.getPermissionInfo(permissionName,
                 0);
         return permissionInfo != null
@@ -1050,16 +1057,19 @@
     private @NonNull
     InstantAppInfo peekOrParseUninstalledInstantAppInfo(
             @NonNull String packageName, @UserIdInt int userId) {
-        if (mUninstalledInstantApps != null) {
-            List<UninstalledInstantAppState> uninstalledAppStates =
-                    mUninstalledInstantApps.get(userId);
-            if (uninstalledAppStates != null) {
-                final int appCount = uninstalledAppStates.size();
-                for (int i = 0; i < appCount; i++) {
-                    UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
-                    if (uninstalledAppState.mInstantAppInfo
-                            .getPackageName().equals(packageName)) {
-                        return uninstalledAppState.mInstantAppInfo;
+        synchronized (mLock) {
+            if (mUninstalledInstantApps != null) {
+                List<UninstalledInstantAppState> uninstalledAppStates =
+                        mUninstalledInstantApps.get(userId);
+                if (uninstalledAppStates != null) {
+                    final int appCount = uninstalledAppStates.size();
+                    for (int i = 0; i < appCount; i++) {
+                        UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(
+                                i);
+                        if (uninstalledAppState.mInstantAppInfo
+                                .getPackageName().equals(packageName)) {
+                            return uninstalledAppState.mInstantAppInfo;
+                        }
                     }
                 }
             }
@@ -1075,14 +1085,15 @@
         return uninstalledAppState.mInstantAppInfo;
     }
 
-    @GuardedBy("mService.mLock")
-    private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr(
-            @UserIdInt int userId) {
+    @Nullable
+    private List<UninstalledInstantAppState> getUninstalledInstantAppStates(@UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates = null;
-        if (mUninstalledInstantApps != null) {
-            uninstalledAppStates = mUninstalledInstantApps.get(userId);
-            if (uninstalledAppStates != null) {
-                return uninstalledAppStates;
+        synchronized (mLock) {
+            if (mUninstalledInstantApps != null) {
+                uninstalledAppStates = mUninstalledInstantApps.get(userId);
+                if (uninstalledAppStates != null) {
+                    return uninstalledAppStates;
+                }
             }
         }
 
@@ -1109,7 +1120,9 @@
             }
         }
 
-        mUninstalledInstantApps.put(userId, uninstalledAppStates);
+        synchronized (mLock) {
+            mUninstalledInstantApps.put(userId, uninstalledAppStates);
+        }
 
         return uninstalledAppStates;
     }
@@ -1246,7 +1259,7 @@
 
             serializer.startTag(null, TAG_PACKAGE);
             serializer.attribute(null, ATTR_LABEL, instantApp.loadLabel(
-                    mService.mContext.getPackageManager()).toString());
+                    mContext.getPackageManager()).toString());
 
             serializer.startTag(null, TAG_PERMISSIONS);
             for (String permission : instantApp.getRequestedPermissions()) {
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index db346da..afca350 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -31,6 +31,7 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.utils.WatchedArrayMap;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -353,9 +354,9 @@
         return mKeySets.get(id) != null;
     }
 
-    public boolean shouldCheckUpgradeKeySetLocked(PackageSetting oldPs, int scanFlags) {
+    public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs, int scanFlags) {
         // Can't rotate keys during boot or if sharedUser.
-        if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()
+        if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || (oldPs.getSharedUser() != null)
                 || !oldPs.getKeySetData().isUsingUpgradeKeySets()) {
             return false;
         }
@@ -374,7 +375,7 @@
         return true;
     }
 
-    public boolean checkUpgradeKeySetLocked(PackageSetting oldPS, AndroidPackage pkg) {
+    public boolean checkUpgradeKeySetLocked(PackageStateInternal oldPS, AndroidPackage pkg) {
         // Upgrade keysets are being used.  Determine if new package has a superset of the
         // required keys.
         long[] upgradeKeySets = oldPS.getKeySetData().getUpgradeKeySets();
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index c125fe1..bd00914 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -30,6 +30,7 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.storage.StorageManager;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 
@@ -376,12 +377,13 @@
         }
 
         // Make a copy of all packages and look into each package.
-        final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
-        final ArrayList<AndroidPackage> pkgs = new ArrayList<>();
-        pmInt.forEachPackage(pkgs::add);
+        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+                LocalServices.getService(PackageManagerInternal.class).getPackageStates();
         int packagePaths = 0;
         int pathsSuccessful = 0;
-        for (AndroidPackage pkg : pkgs) {
+        for (int index = 0; index < packageStates.size(); index++) {
+            final PackageStateInternal packageState = packageStates.valueAt(index);
+            final AndroidPackage pkg = packageState.getPkg();
             if (pkg == null) {
                 continue;
             }
@@ -404,10 +406,9 @@
                 continue;
             }
 
-            PackageStateInternal pkgSetting = pmInt.getPackageStateInternal(pkg.getPackageName());
             final String[] instructionSets = getAppDexInstructionSets(
-                    AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
-                    AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
+                    AndroidPackageUtils.getPrimaryCpuAbi(pkg, packageState),
+                    AndroidPackageUtils.getSecondaryCpuAbi(pkg, packageState));
             final List<String> paths =
                     AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
             final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index d1ea41a..ec71940 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -34,9 +34,7 @@
 import static com.android.server.pm.PackageManagerService.POST_INSTALL;
 import static com.android.server.pm.PackageManagerService.PRUNE_UNUSED_STATIC_SHARED_LIBRARIES;
 import static com.android.server.pm.PackageManagerService.SEND_PENDING_BROADCAST;
-import static com.android.server.pm.PackageManagerService.SNAPSHOT_UNCORK;
 import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerService.TRACE_SNAPSHOTS;
 import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_LIST;
 import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_RESTRICTIONS;
 import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS;
@@ -379,13 +377,6 @@
                 mPm.mDomainVerificationManager.runMessage(messageCode, object);
                 break;
             }
-            case SNAPSHOT_UNCORK: {
-                int corking = mPm.sSnapshotCorked.decrementAndGet();
-                if (TRACE_SNAPSHOTS && corking == 0) {
-                    Log.e(TAG, "snapshot: corking goes to zero in message handler");
-                }
-                break;
-            }
             case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
                 try {
                     mPm.mInjector.getSharedLibrariesImpl().pruneUnusedStaticSharedLibraries(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0d404a..79cfa06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -110,7 +110,6 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageManager.PropertyLocation;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackagePartitions;
 import android.content.pm.ParceledListSlice;
@@ -232,11 +231,14 @@
 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.SharedUserApi;
 import com.android.server.pm.pkg.component.ParsedInstrumentation;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
 import com.android.server.pm.pkg.mutate.PackageStateWrite;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
@@ -592,12 +594,12 @@
     // the suffix "Locked". Some methods may use the legacy suffix "LP"
     final PackageManagerTracedLock mLock;
 
+    // Ensures order of overlay updates until data storage can be moved to overlay code
+    private final PackageManagerTracedLock mOverlayPathsLock = new PackageManagerTracedLock();
+
     // Lock alias for doing package state mutation
     private final PackageManagerTracedLock mPackageStateWriteLock;
 
-    // Lock alias to track syncing a consistent Computer
-    private final PackageManagerTracedLock mLiveComputerSyncLock;
-
     private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
             this::getPackageSettingForMutation,
             this::getDisabledPackageSettingForMutation);
@@ -682,23 +684,11 @@
     @Watched
     final InstantAppRegistry mInstantAppRegistry;
 
-    @GuardedBy("mLock")
-    int mChangedPackagesSequenceNumber;
-    /**
-     * List of changed [installed, removed or updated] packages.
-     * mapping from user id -> sequence number -> package name
-     */
-    @GuardedBy("mLock")
-    final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>();
-    /**
-     * The sequence number of the last change to a package.
-     * mapping from user id -> package name -> sequence number
-     */
-    @GuardedBy("mLock")
-    final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
+    @NonNull
+    final ChangedPackagesTracker mChangedPackagesTracker;
 
-    @GuardedBy("mLock")
-    final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
+    @NonNull
+    private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper();
 
     private final ModuleInfoProvider mModuleInfoProvider;
 
@@ -736,21 +726,11 @@
             ApplicationPackageManager.invalidateGetPackagesForUidCache();
             ApplicationPackageManager.disableGetPackagesForUidCache();
             ApplicationPackageManager.invalidateHasSystemFeatureCache();
-
-            // Avoid invalidation-thrashing by preventing cache invalidations from causing property
-            // writes if the cache isn't enabled yet.  We re-enable writes later when we're
-            // done initializing.
-            sSnapshotCorked.incrementAndGet();
             PackageManager.corkPackageInfoCache();
         }
 
         @Override
         public void enablePackageCaches() {
-            // Uncork cache invalidations and allow clients to cache package information.
-            int corking = sSnapshotCorked.decrementAndGet();
-            if (TRACE_SNAPSHOTS && corking == 0) {
-                Log.i(TAG, "snapshot: corking returns to 0");
-            }
             PackageManager.uncorkPackageInfoCache();
         }
     }
@@ -866,8 +846,10 @@
     @Watched
     final ComponentResolver mComponentResolver;
 
-    // List of packages names to keep cached, even if they are uninstalled for all users
-    private List<String> mKeepUninstalledPackages;
+    // Set of packages names to keep cached, even if they are uninstalled for all users
+    @GuardedBy("mKeepUninstalledPackages")
+    @NonNull
+    private final ArraySet<String> mKeepUninstalledPackages = new ArraySet<>();
 
     // Cached reference to IDevicePolicyManager.
     private IDevicePolicyManager mDevicePolicyManager = null;
@@ -904,8 +886,7 @@
     static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
     static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
     static final int DOMAIN_VERIFICATION = 27;
-    static final int SNAPSHOT_UNCORK = 28;
-    static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 29;
+    static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
 
     static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
     private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1018,7 +999,8 @@
                 isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
                 packages = mPackagesSnapshot.snapshot();
                 instrumentation = mInstrumentationSnapshot.snapshot();
-                resolveComponentName = mResolveComponentName.clone();
+                resolveComponentName = mResolveComponentName == null
+                        ? null : mResolveComponentName.clone();
                 resolveActivity = new ActivityInfo(mResolveActivity);
                 instantAppInstallerActivity =
                         (mInstantAppInstallerActivity == null)
@@ -1075,10 +1057,6 @@
     // should only be set true while holding mLock.  However, the attribute id guaranteed
     // to be set false only while mLock and mSnapshotLock are both held.
     private static final AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
-    // If true, the snapshot is corked.  Do not create a new snapshot but use the live
-    // computer.  This throttles snapshot creation during periods of churn in Package
-    // Manager.
-    static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
 
     static final ThreadLocal<ThreadComputer> sThreadComputer =
             ThreadLocal.withInitial(ThreadComputer::new);
@@ -1095,16 +1073,9 @@
      * The snapshot statistics.  These are collected to track performance and to identify
      * situations in which the snapshots are misbehaving.
      */
+    @Nullable
     private final SnapshotStatistics mSnapshotStatistics;
 
-    // The snapshot disable/enable switch.  An image with the flag set true uses snapshots
-    // and an image with the flag set false does not use snapshots.
-    private static final boolean SNAPSHOT_ENABLED = true;
-
-    // The per-instance snapshot disable/enable flag.  This is generally set to false in
-    // test instances and set to SNAPSHOT_ENABLED in operational instances.
-    private final boolean mSnapshotEnabled;
-
     /**
      * Return the live computer.
      */
@@ -1117,17 +1088,10 @@
      * The live computer will be returned if snapshots are disabled.
      */
     Computer snapshotComputer() {
-        if (!mSnapshotEnabled) {
-            return mLiveComputer;
-        }
         if (Thread.holdsLock(mLock)) {
             // If the current thread holds mLock then it may have modified state but not
             // yet invalidated the snapshot.  Always give the thread the live computer.
             return mLiveComputer;
-        } else if (sSnapshotCorked.get() > 0) {
-            // Snapshots are corked, which means new ones should not be built right now.
-            mSnapshotStatistics.corked();
-            return mLiveComputer;
         }
         synchronized (mSnapshotLock) {
             // This synchronization block serializes access to the snapshot computer and
@@ -1168,7 +1132,9 @@
         mSnapshotComputer = new ComputerEngine(args);
         final long done = SystemClock.currentTimeMicro();
 
-        mSnapshotStatistics.rebuild(now, done, hits);
+        if (mSnapshotStatistics != null) {
+            mSnapshotStatistics.rebuild(now, done, hits);
+        }
     }
 
     /**
@@ -1397,32 +1363,41 @@
         }
     }
 
-    void scheduleWritePackageRestrictionsLocked(UserHandle user) {
+    void scheduleWritePackageRestrictions(UserHandle user) {
         final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
-        scheduleWritePackageRestrictionsLocked(userId);
+        scheduleWritePackageRestrictions(userId);
     }
 
-    void scheduleWritePackageRestrictionsLocked(int userId) {
+    void scheduleWritePackageRestrictions(int userId) {
         invalidatePackageInfoCache();
-        final int[] userIds = (userId == UserHandle.USER_ALL)
-                ? mUserManager.getUserIds() : new int[]{userId};
-        for (int nextUserId : userIds) {
-            if (!mUserManager.exists(nextUserId)) return;
-
-            mDirtyUsers.add(nextUserId);
-            if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
-                mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+        if (userId == UserHandle.USER_ALL) {
+            synchronized (mDirtyUsers) {
+                for (int aUserId : mUserManager.getUserIds()) {
+                    mDirtyUsers.add(aUserId);
+                }
             }
+        } else {
+            if (!mUserManager.exists(userId)) {
+                return;
+            }
+            synchronized (mDirtyUsers) {
+                mDirtyUsers.add(userId);
+            }
+        }
+        if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
+            mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
         }
     }
 
     void writePendingRestrictions() {
         synchronized (mLock) {
             mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-            for (int userId : mDirtyUsers) {
-                mSettings.writePackageRestrictionsLPr(userId);
+            synchronized (mDirtyUsers) {
+                for (int userId : mDirtyUsers) {
+                    mSettings.writePackageRestrictionsLPr(userId);
+                }
+                mDirtyUsers.clear();
             }
-            mDirtyUsers.clear();
         }
     }
 
@@ -1431,7 +1406,9 @@
             mHandler.removeMessages(WRITE_SETTINGS);
             mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
             writeSettingsLPrTEMP();
-            mDirtyUsers.clear();
+            synchronized (mDirtyUsers) {
+                mDirtyUsers.clear();
+            }
         }
     }
 
@@ -1523,25 +1500,19 @@
 
         PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
                 PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
-                Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL, SNAPSHOT_ENABLED);
+                Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
         t.traceEnd(); // "create package manager"
 
         final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
             synchronized (m.mInstallLock) {
-                final AndroidPackage pkg;
-                final PackageSetting ps;
-                final SharedUserSetting sharedUser;
-                final String oldSeInfo;
-                synchronized (m.mLock) {
-                    ps = m.mSettings.getPackageLPr(packageName);
-                    if (ps == null) {
-                        Slog.e(TAG, "Failed to find package setting " + packageName);
-                        return;
-                    }
-                    pkg = ps.getPkg();
-                    sharedUser = ps.getSharedUser();
-                    oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
+                final PackageStateInternal packageState = m.getPackageStateInternal(packageName);
+                if (packageState == null) {
+                    Slog.e(TAG, "Failed to find package setting " + packageName);
+                    return;
                 }
+                AndroidPackage pkg = packageState.getPkg();
+                SharedUserApi sharedUser = packageState.getSharedUser();
+                String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
 
                 if (pkg == null) {
                     Slog.e(TAG, "Failed to find package " + packageName);
@@ -1553,7 +1524,8 @@
                 if (!newSeInfo.equals(oldSeInfo)) {
                     Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
                             + oldSeInfo + " to: " + newSeInfo);
-                    ps.getPkgState().setOverrideSeInfo(newSeInfo);
+                    m.commitPackageStateMutation(null, packageName,
+                            state -> state.setOverrideSeInfo(newSeInfo));
                     m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
                 }
             }
@@ -1573,31 +1545,51 @@
 
     /** Install/uninstall system packages for all users based on their user-type, as applicable. */
     private void installAllowlistedSystemPackages() {
-        synchronized (mLock) {
-            final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
-                    isFirstBoot(), isDeviceUpgrading(), mExistingPackages);
-            if (scheduleWrite) {
-                scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL);
-                scheduleWriteSettings();
-            }
+        if (mUserManager.installWhitelistedSystemPackages(isFirstBoot(), isDeviceUpgrading(),
+                mExistingPackages)) {
+            scheduleWritePackageRestrictions(UserHandle.USER_ALL);
+            scheduleWriteSettings();
         }
     }
 
     // Link watchables to the class
-    private void registerObserver() {
-        mPackages.registerObserver(mWatcher);
-        mSharedLibraries.registerObserver(mWatcher);
-        mInstrumentation.registerObserver(mWatcher);
-        mWebInstantAppsDisabled.registerObserver(mWatcher);
-        mAppsFilter.registerObserver(mWatcher);
-        mInstantAppRegistry.registerObserver(mWatcher);
-        mSettings.registerObserver(mWatcher);
-        mIsolatedOwners.registerObserver(mWatcher);
-        mComponentResolver.registerObserver(mWatcher);
-        mFrozenPackages.registerObserver(mWatcher);
-        // If neither "build" attribute is true then this may be a mockito test, and verification
-        // can fail as a false positive.
-        Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+    private void registerObservers(boolean verify) {
+        // Null check to handle nullable test parameters
+        if (mPackages != null) {
+            mPackages.registerObserver(mWatcher);
+        }
+        if (mSharedLibraries != null) {
+            mSharedLibraries.registerObserver(mWatcher);
+        }
+        if (mInstrumentation != null) {
+            mInstrumentation.registerObserver(mWatcher);
+        }
+        if (mWebInstantAppsDisabled != null) {
+            mWebInstantAppsDisabled.registerObserver(mWatcher);
+        }
+        if (mAppsFilter != null) {
+            mAppsFilter.registerObserver(mWatcher);
+        }
+        if (mInstantAppRegistry != null) {
+            mInstantAppRegistry.registerObserver(mWatcher);
+        }
+        if (mSettings != null) {
+            mSettings.registerObserver(mWatcher);
+        }
+        if (mIsolatedOwners != null) {
+            mIsolatedOwners.registerObserver(mWatcher);
+        }
+        if (mComponentResolver != null) {
+            mComponentResolver.registerObserver(mWatcher);
+        }
+        if (mFrozenPackages != null) {
+            mFrozenPackages.registerObserver(mWatcher);
+        }
+        if (verify) {
+            // If neither "build" attribute is true then this may be a mockito test,
+            // and verification can fail as a false positive.
+            Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
+        }
     }
 
     /**
@@ -1619,7 +1611,6 @@
         mInstallLock = injector.getInstallLock();
         mLock = injector.getLock();
         mPackageStateWriteLock = mLock;
-        mLiveComputerSyncLock = mLock;
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
         mUserManager = injector.getUserManagerService();
@@ -1639,6 +1630,7 @@
         mIncrementalManager = testParams.incrementalManager;
         mInstallerService = testParams.installerService;
         mInstantAppRegistry = testParams.instantAppRegistry;
+        mChangedPackagesTracker = testParams.changedPackagesTracker;
         mInstantAppResolverConnection = testParams.instantAppResolverConnection;
         mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
         mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
@@ -1678,10 +1670,6 @@
         mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
         mResolveComponentName = testParams.resolveComponentName;
 
-        // Disable snapshots in this instance of PackageManagerService, which is only used
-        // for testing.  The instance still needs a live computer.  The snapshot computer
-        // is set to null since it must never be used by this instance.
-        mSnapshotEnabled = false;
         mLiveComputer = createLiveComputer();
         mSnapshotComputer = null;
         mSnapshotStatistics = null;
@@ -1707,13 +1695,13 @@
         mSuspendPackageHelper = testParams.suspendPackageHelper;
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
 
+        registerObservers(false);
         invalidatePackageInfoCache();
     }
 
     public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
             boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
-            final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion,
-            boolean snapshotEnabled) {
+            final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
         mIsEngBuild = isEngBuild;
         mIsUserDebugBuild = isUserDebugBuild;
         mSdkVersion = sdkVersion;
@@ -1728,7 +1716,6 @@
         mInjector.bootstrap(this);
         mLock = injector.getLock();
         mPackageStateWriteLock = mLock;
-        mLiveComputerSyncLock = mLock;
         mInstallLock = injector.getInstallLock();
         LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -1832,7 +1819,10 @@
         mApexManager = injector.getApexManager();
         mAppsFilter = mInjector.getAppsFilter();
 
-        mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
+        mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
+                mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
+
+        mChangedPackagesTracker = new ChangedPackagesTracker();
 
         mAppInstallDir = new File(Environment.getDataDirectory(), "app");
 
@@ -1857,16 +1847,12 @@
         synchronized (mLock) {
             // Create the computer as soon as the state objects have been installed.  The
             // cached computer is the same as the live computer until the end of the
-            // constructor, at which time the invalidation method updates it.  The cache is
-            // corked initially to ensure a cached computer is not built until the end of the
-            // constructor.
+            // constructor, at which time the invalidation method updates it.
             mSnapshotStatistics = new SnapshotStatistics();
-            sSnapshotCorked.set(1);
             sSnapshotInvalid.set(true);
             mLiveComputer = createLiveComputer();
             mSnapshotComputer = null;
-            mSnapshotEnabled = snapshotEnabled;
-            registerObserver();
+            registerObservers(true);
         }
 
         // CHECKSTYLE:OFF IndentationCheck
@@ -2161,7 +2147,7 @@
                             || !ps.getUserStateOrDefault(userId).isInstalled()) {
                         continue;
                     }
-                    mInstantAppRegistry.addInstantAppLPw(userId, ps.getAppId());
+                    mInstantAppRegistry.addInstantApp(userId, ps.getAppId());
                 }
             }
 
@@ -2852,11 +2838,17 @@
             // TODO: Implement
 
             // 7. Consider installed instant apps unused longer than min cache period
-            if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes,
-                    android.provider.Settings.Global.getLong(mContext.getContentResolver(),
-                            Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
-                            InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
-                return;
+            if (internalVolume) {
+                if (executeWithConsistentComputerReturning(computer ->
+                        mInstantAppRegistry.pruneInstalledInstantApps(computer, bytes,
+                                android.provider.Settings.Global.getLong(
+                                        mContext.getContentResolver(),
+                                        Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                                        InstantAppRegistry
+                                                .DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD)))
+                ) {
+                    return;
+                }
             }
 
             // 8. Consider cached app data (below quotas)
@@ -2873,11 +2865,17 @@
             // TODO: Implement
 
             // 10. Consider instant meta-data (uninstalled apps) older that min cache period
-            if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes,
-                    android.provider.Settings.Global.getLong(mContext.getContentResolver(),
-                            Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
-                            InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
-                return;
+            if (internalVolume) {
+                if (executeWithConsistentComputerReturning(computer ->
+                        mInstantAppRegistry.pruneUninstalledInstantApps(computer, bytes,
+                                android.provider.Settings.Global.getLong(
+                                        mContext.getContentResolver(),
+                                        Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                                        InstantAppRegistry
+                                                .DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD)))
+                ) {
+                    return;
+                }
             }
 
             // 11. Free storage service cache
@@ -3041,26 +3039,7 @@
 
     @GuardedBy("mLock")
     void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
-        for (int i = userList.length - 1; i >= 0; --i) {
-            final int userId = userList[i];
-            SparseArray<String> changedPackages = mChangedPackages.get(userId);
-            if (changedPackages == null) {
-                changedPackages = new SparseArray<>();
-                mChangedPackages.put(userId, changedPackages);
-            }
-            Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
-            if (sequenceNumbers == null) {
-                sequenceNumbers = new HashMap<>();
-                mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
-            }
-            final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.getPackageName());
-            if (sequenceNumber != null) {
-                changedPackages.remove(sequenceNumber);
-            }
-            changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.getPackageName());
-            sequenceNumbers.put(pkgSetting.getPackageName(), mChangedPackagesSequenceNumber);
-        }
-        mChangedPackagesSequenceNumber++;
+        mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList);
     }
 
     @Override
@@ -3073,30 +3052,21 @@
             return null;
         }
         enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
-        synchronized (mLock) {
-            if (sequenceNumber >= mChangedPackagesSequenceNumber) {
-                return null;
-            }
-            final SparseArray<String> changedPackages = mChangedPackages.get(userId);
-            if (changedPackages == null) {
-                return null;
-            }
-            final List<String> packageNames =
-                    new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
-            for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
-                final String packageName = changedPackages.get(i);
-                if (packageName != null) {
-                    // Filter out the changes if the calling package should not be able to see it.
-                    final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                    if (shouldFilterApplication(ps, callingUid, userId)) {
-                        continue;
-                    }
-                    packageNames.add(packageName);
+        final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages(
+                sequenceNumber, userId);
+
+        if (changedPackages != null) {
+            final List<String> packageNames = changedPackages.getPackageNames();
+            for (int index = packageNames.size() - 1; index >= 0; index--) {
+                // Filter out the changes if the calling package should not be able to see it.
+                final PackageSetting ps = mSettings.getPackageLPr(packageNames.get(index));
+                if (shouldFilterApplication(ps, callingUid, userId)) {
+                    packageNames.remove(index);
                 }
             }
-            return packageNames.isEmpty()
-                    ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
         }
+
+        return changedPackages;
     }
 
     @Override
@@ -3143,8 +3113,8 @@
     @Override
     public String getPermissionControllerPackageName() {
         final int callingUid = Binder.getCallingUid();
-        if (mComputer.isPackageStateAvailableAndVisible(mRequiredPermissionControllerPackage,
-                callingUid, UserHandle.getUserId(callingUid))) {
+        if (mComputer.getPackageStateFiltered(mRequiredPermissionControllerPackage,
+                callingUid, UserHandle.getUserId(callingUid)) != null) {
             return mRequiredPermissionControllerPackage;
         }
 
@@ -3533,12 +3503,11 @@
         }
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
                 false /* checkShell */, "getEphemeralApplications");
-        synchronized (mLock) {
-            List<InstantAppInfo> instantApps = mInstantAppRegistry
-                    .getInstantAppsLPr(userId);
-            if (instantApps != null) {
-                return new ParceledListSlice<>(instantApps);
-            }
+
+        List<InstantAppInfo> instantApps = executeWithConsistentComputerReturning(computer ->
+                mInstantAppRegistry.getInstantApps(computer, userId));
+        if (instantApps != null) {
+            return new ParceledListSlice<>(instantApps);
         }
         return null;
     }
@@ -3565,10 +3534,11 @@
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return null;
         }
-        synchronized (mLock) {
-            return mInstantAppRegistry.getInstantAppCookieLPw(
-                    packageName, userId);
+        PackageStateInternal packageState = getPackageStateInternal(packageName);
+        if (packageState == null || packageState.getPkg() == null) {
+            return null;
         }
+        return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId);
     }
 
     @Override
@@ -3582,10 +3552,13 @@
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return false;
         }
-        synchronized (mLock) {
-            return mInstantAppRegistry.setInstantAppCookieLPw(
-                    packageName, cookie, userId);
+
+        PackageStateInternal packageState = getPackageStateInternal(packageName);
+        if (packageState == null || packageState.getPkg() == null) {
+            return false;
         }
+        return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie,
+                mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId);
     }
 
     @Override
@@ -3601,10 +3574,7 @@
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
                 false /* checkShell */, "getInstantAppIcon");
 
-        synchronized (mLock) {
-            return mInstantAppRegistry.getInstantAppIconLPw(
-                    packageName, userId);
-        }
+        return mInstantAppRegistry.getInstantAppIcon(packageName, userId);
     }
 
     boolean isCallerSameApp(String packageName, int uid) {
@@ -3722,16 +3692,14 @@
                     }
                 }
                 if (doTrim) {
-                    final boolean dexOptDialogShown;
-                    synchronized (mLock) {
-                        dexOptDialogShown = mDexOptHelper.isDexOptDialogShown();
-                    }
-                    if (!isFirstBoot() && dexOptDialogShown) {
-                        try {
-                            ActivityManager.getService().showBootMessage(
-                                    mContext.getResources().getString(
-                                            R.string.android_upgrading_fstrim), true);
-                        } catch (RemoteException e) {
+                    if (!isFirstBoot()) {
+                        if (mDexOptHelper.isDexOptDialogShown()) {
+                            try {
+                                ActivityManager.getService().showBootMessage(
+                                        mContext.getResources().getString(
+                                                R.string.android_upgrading_fstrim), true);
+                            } catch (RemoteException e) {
+                            }
                         }
                     }
                     sm.runMaintenance();
@@ -3751,29 +3719,27 @@
 
     @Override
     public void notifyPackageUse(String packageName, int reason) {
-        synchronized (mLock) {
-            final int callingUid = Binder.getCallingUid();
-            final int callingUserId = UserHandle.getUserId(callingUid);
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        boolean notify = executeWithConsistentComputerReturning(computer -> {
             if (getInstantAppPackageName(callingUid) != null) {
-                if (!isCallerSameApp(packageName, callingUid)) {
-                    return;
-                }
+                return isCallerSameApp(packageName, callingUid);
             } else {
-                if (isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID)) {
-                    return;
-                }
+                return !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
             }
-            notifyPackageUseLocked(packageName, reason);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void notifyPackageUseLocked(String packageName, int reason) {
-        final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
-        if (pkgSetting == null) {
+        });
+        if (!notify) {
             return;
         }
-        pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
+
+        notifyPackageUseInternal(packageName, reason);
+    }
+
+    private void notifyPackageUseInternal(String packageName, int reason) {
+        long time = System.currentTimeMillis();
+        commitPackageStateMutation(null, packageName, packageState -> {
+            packageState.setLastPackageUsageTime(reason, time);
+        });
     }
 
     @Override
@@ -3899,10 +3865,12 @@
             // This is the last chance to write out pending restriction settings
             if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
                 mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-                for (int userId : mDirtyUsers) {
-                    mSettings.writePackageRestrictionsLPr(userId);
+                synchronized (mDirtyUsers) {
+                    for (int userId : mDirtyUsers) {
+                        mSettings.writePackageRestrictionsLPr(userId);
+                    }
+                    mDirtyUsers.clear();
                 }
-                mDirtyUsers.clear();
             }
         }
     }
@@ -3918,12 +3886,9 @@
             throw new SecurityException("dumpProfiles");
         }
 
-        AndroidPackage pkg;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
+        AndroidPackage pkg = getPackage(packageName);
+        if (pkg == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
         }
 
         synchronized (mInstallLock) {
@@ -3946,14 +3911,12 @@
     public Property getProperty(String propertyName, String packageName, String className) {
         Objects.requireNonNull(propertyName);
         Objects.requireNonNull(packageName);
-        synchronized (mLock) {
-            final PackageStateInternal ps = getPackageStateInternal(packageName);
-            if (shouldFilterApplication(ps, Binder.getCallingUid(),
-                    UserHandle.getCallingUserId())) {
-                return null;
-            }
-            return mPackageProperty.getProperty(propertyName, packageName, className);
+        PackageStateInternal packageState = mComputer.getPackageStateFiltered(packageName,
+                Binder.getCallingUid(), UserHandle.getCallingUserId());
+        if (packageState == null) {
+            return null;
         }
+        return mPackageProperty.getProperty(propertyName, packageName, className);
     }
 
     @Override
@@ -4038,66 +4001,33 @@
 
     @Override
     public void notifyPackageAdded(String packageName, int uid) {
-        final PackageListObserver[] observers;
-        synchronized (mLock) {
-            if (mPackageListObservers.size() == 0) {
-                return;
-            }
-            final PackageListObserver[] observerArray =
-                    new PackageListObserver[mPackageListObservers.size()];
-            observers = mPackageListObservers.toArray(observerArray);
-        }
-        for (int i = observers.length - 1; i >= 0; --i) {
-            observers[i].onPackageAdded(packageName, uid);
-        }
+        mPackageObserverHelper.notifyAdded(packageName, uid);
     }
 
     @Override
     public void notifyPackageChanged(String packageName, int uid) {
-        final PackageListObserver[] observers;
-        synchronized (mLock) {
-            if (mPackageListObservers.size() == 0) {
-                return;
-            }
-            final PackageListObserver[] observerArray =
-                    new PackageListObserver[mPackageListObservers.size()];
-            observers = mPackageListObservers.toArray(observerArray);
-        }
-        for (int i = observers.length - 1; i >= 0; --i) {
-            observers[i].onPackageChanged(packageName, uid);
-        }
+        mPackageObserverHelper.notifyChanged(packageName, uid);
     }
 
     @Override
     public void notifyPackageRemoved(String packageName, int uid) {
-        final PackageListObserver[] observers;
-        synchronized (mLock) {
-            if (mPackageListObservers.size() == 0) {
-                return;
-            }
-            final PackageListObserver[] observerArray =
-                    new PackageListObserver[mPackageListObservers.size()];
-            observers = mPackageListObservers.toArray(observerArray);
-        }
-        for (int i = observers.length - 1; i >= 0; --i) {
-            observers[i].onPackageRemoved(packageName, uid);
-        }
+        mPackageObserverHelper.notifyRemoved(packageName, uid);
     }
 
-    void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
+    void sendPackageAddedForUser(String packageName, @NonNull PackageStateInternal packageState,
             int userId, int dataLoaderType) {
-        final boolean isSystem = PackageManagerServiceUtils.isSystemApp(pkgSetting)
-                || PackageManagerServiceUtils.isUpdatedSystemApp(pkgSetting);
-        final boolean isInstantApp = pkgSetting.getInstantApp(userId);
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+        final boolean isSystem = packageState.isSystem();
+        final boolean isInstantApp = userState.isInstantApp();
         final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
         final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
         sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
-                false /*startReceiver*/, pkgSetting.getAppId(), userIds, instantUserIds,
+                false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
                 dataLoaderType);
 
         // Send a session commit broadcast
         final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
-        info.installReason = pkgSetting.getInstallReason(userId);
+        info.installReason = userState.getInstallReason();
         info.appPackageName = packageName;
         sendSessionCommitBroadcast(info, userId);
     }
@@ -4129,7 +4059,6 @@
     public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
             int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-        PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
         enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                 true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
@@ -4139,74 +4068,70 @@
             return false;
         }
 
+        // Do not allow "android" is being disabled
+        if ("android".equals(packageName)) {
+            Slog.w(TAG, "Cannot hide package: android");
+            return false;
+        }
+
         final long callingId = Binder.clearCallingIdentity();
         try {
-            boolean sendAdded = false;
-            boolean sendRemoved = false;
-            // writer
-            synchronized (mLock) {
-                pkgSetting = mSettings.getPackageLPr(packageName);
-                if (pkgSetting == null) {
-                    return false;
-                }
-                if (shouldFilterApplication(pkgSetting, callingUid, userId)) {
-                    return false;
-                }
-                // Do not allow "android" is being disabled
-                if ("android".equals(packageName)) {
-                    Slog.w(TAG, "Cannot hide package: android");
-                    return false;
-                }
-                AndroidPackage pkg = mPackages.get(packageName);
-                if (pkg != null) {
-                    // Cannot hide SDK libs as they are controlled by SDK manager.
-                    if (pkg.getSdkLibName() != null) {
-                        Slog.w(TAG, "Cannot hide package: " + packageName
-                                + " providing SDK library: "
-                                + pkg.getSdkLibName());
-                        return false;
-                    }
-                    // Cannot hide static shared libs as they are considered
-                    // a part of the using app (emulating static linking). Also
-                    // static libs are installed always on internal storage.
-                    if (pkg.getStaticSharedLibName() != null) {
-                        Slog.w(TAG, "Cannot hide package: " + packageName
-                                + " providing static shared library: "
-                                + pkg.getStaticSharedLibName());
-                        return false;
-                    }
-                }
-                // Only allow protected packages to hide themselves.
-                if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.getAppId())
-                        && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-                    Slog.w(TAG, "Not hiding protected package: " + packageName);
-                    return false;
-                }
+            final PackageStateInternal packageState =
+                    mComputer.getPackageStateFiltered(packageName, callingUid, userId);
+            if (packageState == null) {
+                return false;
+            }
 
-                if (pkgSetting.getHidden(userId) != hidden) {
-                    pkgSetting.setHidden(hidden, userId);
-                    mSettings.writePackageRestrictionsLPr(userId);
-                    if (hidden) {
-                        sendRemoved = true;
-                    } else {
-                        sendAdded = true;
-                    }
+            // Cannot hide static shared libs as they are considered
+            // a part of the using app (emulating static linking). Also
+            // static libs are installed always on internal storage.
+            AndroidPackage pkg = packageState.getPkg();
+            if (pkg != null) {
+                // Cannot hide SDK libs as they are controlled by SDK manager.
+                if (pkg.getSdkLibName() != null) {
+                    Slog.w(TAG, "Cannot hide package: " + packageName
+                            + " providing SDK library: "
+                            + pkg.getSdkLibName());
+                    return false;
+                }
+                // Cannot hide static shared libs as they are considered
+                // a part of the using app (emulating static linking). Also
+                // static libs are installed always on internal storage.
+                if (pkg.getStaticSharedLibName() != null) {
+                    Slog.w(TAG, "Cannot hide package: " + packageName
+                            + " providing static shared library: "
+                            + pkg.getStaticSharedLibName());
+                    return false;
                 }
             }
-            if (sendAdded) {
-                sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
-                return true;
+            // Only allow protected packages to hide themselves.
+            if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId())
+                    && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                Slog.w(TAG, "Not hiding protected package: " + packageName);
+                return false;
             }
-            if (sendRemoved) {
-                killApplication(packageName, pkgSetting.getAppId(), userId,
-                        "hiding pkg");
-                sendApplicationHiddenForUser(packageName, pkgSetting, userId);
-                return true;
+
+            if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
+                return false;
             }
+
+            commitPackageStateMutation(null, packageName, packageState1 ->
+                    packageState1.userState(userId).setHidden(hidden));
+
+            final PackageStateInternal newPackageState = getPackageStateInternal(packageName);
+
+            if (hidden) {
+                killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg");
+                sendApplicationHiddenForUser(packageName, newPackageState, userId);
+            } else {
+                sendPackageAddedForUser(packageName, newPackageState, userId, DataLoaderType.NONE);
+            }
+
+            scheduleWritePackageRestrictions(userId);
+            return true;
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
-        return false;
     }
 
     @Override
@@ -4219,21 +4144,20 @@
                     "setSystemAppHiddenUntilInstalled");
         }
 
-        synchronized (mLock) {
-            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
-            if (pkgSetting == null || !pkgSetting.isSystem()) {
-                return;
-            }
-            if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
-                throw new SecurityException("Only system or phone callers can modify core apps");
-            }
-            pkgSetting.getPkgState().setHiddenUntilInstalled(hidden);
-            final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
-            if (disabledPs == null) {
-                return;
-            }
-            disabledPs.getPkgState().setHiddenUntilInstalled(hidden);
+        final PackageStateInternal stateRead = getPackageStateInternal(packageName);
+        if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) {
+            return;
         }
+        if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+            throw new SecurityException("Only system or phone callers can modify core apps");
+        }
+
+        commitPackageStateMutation(null, mutator -> {
+            mutator.forPackage(packageName)
+                    .setHiddenUntilInstalled(hidden);
+            mutator.forDisabledSystemPackage(packageName)
+                    .setHiddenUntilInstalled(hidden);
+        });
     }
 
     @Override
@@ -4246,19 +4170,17 @@
                     "setSystemAppHiddenUntilInstalled");
         }
 
-        synchronized (mLock) {
-            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
-            // The target app should always be in system
-            if (pkgSetting == null || !pkgSetting.isSystem()) {
-                return false;
-            }
-            if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
-                throw new SecurityException("Only system or phone callers can modify core apps");
-            }
-            // Check if the install state is the same
-            if (pkgSetting.getInstalled(userId) == installed) {
-                return false;
-            }
+        final PackageStateInternal packageState = getPackageStateInternal(packageName);
+        // The target app should always be in system
+        if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
+            return false;
+        }
+        if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+            throw new SecurityException("Only system or phone callers can modify core apps");
+        }
+        // Check if the install state is the same
+        if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
+            return false;
         }
 
         final long callingId = Binder.clearCallingIdentity();
@@ -4286,14 +4208,14 @@
         }
     }
 
-    private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
+    private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
             int userId) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
         info.mRemovedPackage = packageName;
-        info.mInstallerPackageName = pkgSetting.getInstallSource().installerPackageName;
+        info.mInstallerPackageName = packageState.getInstallSource().installerPackageName;
         info.mRemovedUsers = new int[] {userId};
         info.mBroadcastUsers = new int[] {userId};
-        info.mUid = UserHandle.getUid(userId, pkgSetting.getAppId());
+        info.mUid = UserHandle.getUid(userId, packageState.getAppId());
         info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
     }
 
@@ -4348,45 +4270,52 @@
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final boolean[] canRestrict = (restrictionFlags != 0)
-                ? mSuspendPackageHelper.canSuspendPackageForUser(packageNames, userId, callingUid)
-                : null;
 
-        for (int i = 0; i < packageNames.length; i++) {
-            final String packageName = packageNames[i];
-            final PackageSetting pkgSetting;
-            synchronized (mLock) {
-                pkgSetting = mSettings.getPackageLPr(packageName);
-                if (pkgSetting == null
-                        || shouldFilterApplication(pkgSetting, callingUid, userId)) {
+        ArraySet<String> changesToCommit = new ArraySet<>();
+        executeWithConsistentComputer(computer -> {
+            final boolean[] canRestrict = (restrictionFlags != 0)
+                    ? mSuspendPackageHelper.canSuspendPackageForUser(computer, packageNames, userId,
+                    callingUid) : null;
+            for (int i = 0; i < packageNames.length; i++) {
+                final String packageName = packageNames[i];
+                final PackageStateInternal packageState =
+                        computer.getPackageStateInternal(packageName);
+                if (packageState == null
+                        || shouldFilterApplication(packageState, callingUid, userId)) {
                     Slog.w(TAG, "Could not find package setting for package: " + packageName
                             + ". Skipping...");
                     unactionedPackages.add(packageName);
                     continue;
                 }
-            }
-            if (canRestrict != null && !canRestrict[i]) {
-                unactionedPackages.add(packageName);
-                continue;
-            }
-            synchronized (mLock) {
-                final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
+                if (canRestrict != null && !canRestrict[i]) {
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+                final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
+                        .getDistractionFlags();
                 if (restrictionFlags != oldDistractionFlags) {
-                    pkgSetting.setDistractionFlags(restrictionFlags, userId);
                     changedPackagesList.add(packageName);
-                    changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+                    changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                    changesToCommit.add(packageName);
                 }
             }
-        }
+        });
+
+        commitPackageStateMutation(null, mutator -> {
+            final int size = changesToCommit.size();
+            for (int index = 0; index < size; index++) {
+                mutator.forPackage(changesToCommit.valueAt(index))
+                        .userState(userId)
+                        .setDistractionFlags(restrictionFlags);
+            }
+        });
 
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
             mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
                     changedPackages, changedUids.toArray(), userId, restrictionFlags));
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
+            scheduleWritePackageRestrictions(userId);
         }
         return unactionedPackages.toArray(new String[0]);
     }
@@ -4450,10 +4379,9 @@
     }
 
     void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
-        final String[] allPackages;
-        synchronized (mLock) {
-            allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
-        }
+        // TODO: This can be replaced by a special parameter to iterate all packages, rather than
+        //  this weird pre-collect of all packages.
+        final String[] allPackages = getPackageStates().keySet().toArray(new String[0]);
         mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
                 allPackages, suspendingPackage::equals, userId);
     }
@@ -4479,22 +4407,27 @@
     private void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) {
         final List<String> changedPackages = new ArrayList<>();
         final IntArray changedUids = new IntArray();
-        synchronized (mLock) {
-            for (String packageName : packagesToChange) {
-                final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                if (ps != null && ps.getDistractionFlags(userId) != 0) {
-                    ps.setDistractionFlags(0, userId);
-                    changedPackages.add(ps.getPackageName());
-                    changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
-                }
+        for (String packageName : packagesToChange) {
+            final PackageStateInternal ps = getPackageStateInternal(packageName);
+            if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags() != 0) {
+                changedPackages.add(ps.getPackageName());
+                changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
             }
-            if (!changedPackages.isEmpty()) {
-                final String[] packageArray = changedPackages.toArray(
-                        new String[changedPackages.size()]);
-                mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
-                        packageArray, changedUids.toArray(), userId, 0));
-                scheduleWritePackageRestrictionsLocked(userId);
+        }
+        commitPackageStateMutation(null, mutator -> {
+            for (int index = 0; index < changedPackages.size(); index++) {
+                mutator.forPackage(changedPackages.get(index))
+                        .userState(userId)
+                        .setDistractionFlags(0);
             }
+        });
+
+        if (!changedPackages.isEmpty()) {
+            final String[] packageArray = changedPackages.toArray(
+                    new String[changedPackages.size()]);
+            mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
+                    packageArray, changedUids.toArray(), userId, 0));
+            scheduleWritePackageRestrictions(userId);
         }
     }
 
@@ -4616,43 +4549,40 @@
     public void setInstallerPackageName(String targetPackage, String installerPackageName) {
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        if (getInstantAppPackageName(callingUid) != null) {
-            return;
-        }
-        // writer
-        synchronized (mLock) {
-            PackageSetting targetPackageSetting = mSettings.getPackageLPr(targetPackage);
-            if (targetPackageSetting == null
-                    || shouldFilterApplication(
-                            targetPackageSetting, callingUid, callingUserId)) {
+        final FunctionalUtils.ThrowingCheckedFunction<Computer, Boolean, RuntimeException>
+                implementation = computer -> {
+            if (computer.getInstantAppPackageName(callingUid) != null) {
+                return false;
+            }
+
+            PackageStateInternal targetPackageState =
+                    computer.getPackageStateInternal(targetPackage);
+            if (targetPackageState == null
+                    || computer.shouldFilterApplication(targetPackageState, callingUid,
+                    callingUserId)) {
                 throw new IllegalArgumentException("Unknown target package: " + targetPackage);
             }
 
-            PackageSetting installerPackageSetting;
+            PackageStateInternal installerPackageState = null;
             if (installerPackageName != null) {
-                installerPackageSetting = mSettings.getPackageLPr(installerPackageName);
-                if (installerPackageSetting == null
+                installerPackageState = computer.getPackageStateInternal(installerPackageName);
+                if (installerPackageState == null
                         || shouldFilterApplication(
-                                installerPackageSetting, callingUid, callingUserId)) {
+                        installerPackageState, callingUid, callingUserId)) {
                     throw new IllegalArgumentException("Unknown installer package: "
                             + installerPackageName);
                 }
-            } else {
-                installerPackageSetting = null;
             }
 
             Signature[] callerSignature;
             final int appId = UserHandle.getAppId(callingUid);
-            final Object obj = mSettings.getSettingLPr(appId);
-            if (obj != null) {
-                if (obj instanceof SharedUserSetting) {
-                    callerSignature =
-                            ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
-                } else if (obj instanceof PackageSetting) {
-                    callerSignature =
-                            ((PackageSetting) obj).getSigningDetails().getSignatures();
+            Pair<PackageStateInternal, SharedUserApi> either =
+                    computer.getPackageOrSharedUser(appId);
+            if (either != null) {
+                if (either.first != null) {
+                    callerSignature = either.first.getSigningDetails().getSignatures();
                 } else {
-                    throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
+                    callerSignature = either.second.getSigningDetails().getSignatures();
                 }
             } else {
                 throw new SecurityException("Unknown calling UID: " + callingUid);
@@ -4660,22 +4590,22 @@
 
             // Verify: can't set installerPackageName to a package that is
             // not signed with the same cert as the caller.
-            if (installerPackageSetting != null) {
+            if (installerPackageState != null) {
                 if (compareSignatures(callerSignature,
-                        installerPackageSetting.getSigningDetails().getSignatures())
+                        installerPackageState.getSigningDetails().getSignatures())
                         != PackageManager.SIGNATURE_MATCH) {
                     throw new SecurityException(
                             "Caller does not have same cert as new installer package "
-                            + installerPackageName);
+                                    + installerPackageName);
                 }
             }
 
             // Verify: if target already has an installer package, it must
             // be signed with the same cert as the caller.
             String targetInstallerPackageName =
-                    targetPackageSetting.getInstallSource().installerPackageName;
-            PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
-                    mSettings.getPackageLPr(targetInstallerPackageName);
+                    targetPackageState.getInstallSource().installerPackageName;
+            PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null
+                    ? null : computer.getPackageStateInternal(targetInstallerPackageName);
 
             if (targetInstallerPkgSetting != null) {
                 if (compareSignatures(callerSignature,
@@ -4683,10 +4613,10 @@
                         != PackageManager.SIGNATURE_MATCH) {
                     throw new SecurityException(
                             "Caller does not have same cert as old installer package "
-                            + targetInstallerPackageName);
+                                    + targetInstallerPackageName);
                 }
             } else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
-                    != PackageManager.PERMISSION_GRANTED) {
+                    != PERMISSION_GRANTED) {
                 // This is probably an attempt to exploit vulnerability b/150857253 of taking
                 // privileged installer permissions when the installer has been uninstalled or
                 // was never set.
@@ -4702,17 +4632,39 @@
                                 + Manifest.permission.INSTALL_PACKAGES);
                     } else {
                         // If change disabled, fail silently for backwards compatibility
-                        return;
+                        return false;
                     }
                 } finally {
                     Binder.restoreCallingIdentity(binderToken);
                 }
             }
 
-            // Okay!
-            targetPackageSetting.setInstallerPackageName(installerPackageName);
-            mSettings.addInstallerPackageNames(targetPackageSetting.getInstallSource());
-            mAppsFilter.addPackage(targetPackageSetting);
+            return true;
+        };
+        PackageStateMutator.InitialState initialState = recordInitialState();
+        boolean allowed = executeWithConsistentComputerReturningThrowing(implementation);
+        if (allowed) {
+            // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
+            //  should find an alternative which avoids any race conditions
+            PackageStateInternal targetPackageState;
+            synchronized (mLock) {
+                PackageStateMutator.Result result = commitPackageStateMutation(initialState,
+                        targetPackage, state -> state.setInstaller(installerPackageName));
+                if (result.isPackagesChanged() || result.isStateChanged()) {
+                    synchronized (mPackageStateWriteLock) {
+                        allowed = executeWithConsistentComputerReturningThrowing(implementation);
+                        if (allowed) {
+                            commitPackageStateMutation(null, targetPackage,
+                                    state -> state.setInstaller(installerPackageName));
+                        } else {
+                            return;
+                        }
+                    }
+                }
+                targetPackageState = getPackageStateInternal(targetPackage);
+                mSettings.addInstallerPackageNames(targetPackageState.getInstallSource());
+            }
+            mAppsFilter.addPackage(targetPackageState);
             scheduleWriteSettings();
         }
     }
@@ -4725,21 +4677,42 @@
         }
         mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 callerPackageName);
-        synchronized (mLock) {
-            PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps == null || shouldFilterApplication(
-                    ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
+
+        final PackageStateMutator.InitialState initialState = recordInitialState();
+
+        final FunctionalUtils.ThrowingFunction<Computer, PackageStateMutator.Result>
+                implementation = computer -> {
+            PackageStateInternal packageState = computer.getPackageStateFiltered(packageName,
+                    Binder.getCallingUid(), UserHandle.getCallingUserId());
+            if (packageState == null) {
                 throw new IllegalArgumentException("Unknown target package " + packageName);
             }
-            if (!Objects.equals(callerPackageName, ps.getInstallSource().installerPackageName)) {
+
+            if (!Objects.equals(callerPackageName,
+                    packageState.getInstallSource().installerPackageName)) {
                 throw new IllegalArgumentException("Calling package " + callerPackageName
                         + " is not installer for " + packageName);
             }
 
-            if (ps.getCategoryOverride() != categoryHint) {
-                ps.setCategoryOverride(categoryHint);
-                scheduleWriteSettings();
+            if (packageState.getCategoryOverride() != categoryHint) {
+                return commitPackageStateMutation(initialState,
+                        packageName, state -> state.setCategoryOverride(categoryHint));
+            } else {
+                return null;
             }
+        };
+
+        PackageStateMutator.Result result = executeWithConsistentComputerReturning(implementation);
+        if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) {
+            // TODO: Specific return value of what state changed?
+            // The installer on record might have changed, retry with lock
+            synchronized (mPackageStateWriteLock) {
+                result = executeWithConsistentComputerReturning(implementation);
+            }
+        }
+
+        if (result != null && result.isCommitted()) {
+            scheduleWriteSettings();
         }
     }
 
@@ -4792,32 +4765,6 @@
         });
     }
 
-    // Stop gap method to allow mutating a package setting before commit on write is added
-    private void mutateInstalledPackageSetting(@NonNull String packageName, int callingUid,
-            @UserIdInt int userId,
-            @NonNull FunctionalUtils.ThrowingConsumer<PackageSetting> consumerLocked) {
-        synchronized (mLock) {
-            PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps == null) {
-                Slog.w(TAG, "Failed to get package setting. Package " + packageName
-                        + " is not installed");
-                return;
-            }
-            if (!ps.getInstalled(userId)) {
-                Slog.w(TAG, "Failed to get package setting. Package " + packageName
-                        + " is not installed for user " + userId);
-                return;
-            }
-            if (shouldFilterApplication(ps, callingUid, userId)) {
-                Slog.w(TAG, "Failed to get package setting. Package " + packageName
-                        + " is not visible to the calling app");
-                return;
-            }
-
-            consumerLocked.accept(ps);
-        }
-    }
-
     void notifyPackageChangeObservers(PackageChangeEvent event) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
@@ -4872,9 +4819,8 @@
         return mComputer.resolveExternalPackageName(pkg);
     }
 
-    @GuardedBy("mLock")
-    String resolveInternalPackageNameLPr(String packageName, long versionCode) {
-        return mComputer.resolveInternalPackageNameLPr(packageName, versionCode);
+    String resolveInternalPackageName(String packageName, long versionCode) {
+        return mComputer.resolveInternalPackageName(packageName, versionCode);
     }
 
     boolean isCallerVerifier(int callingUid) {
@@ -4947,28 +4893,29 @@
             int userId) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
-        // TODO (b/157774108): This should fail on non-existent packages.
-        synchronized (mLock) {
-            AndroidPackage pkg = mPackages.get(packageName);
-            if (pkg != null) {
-                // Cannot block uninstall SDK libs as they are controlled by SDK manager.
-                if (pkg.getSdkLibName() != null) {
-                    Slog.w(TAG, "Cannot block uninstall of package: " + packageName
-                            + " providing SDK library: " + pkg.getSdkLibName());
-                    return false;
-                }
-                // Cannot block uninstall of static shared libs as they are
-                // considered a part of the using app (emulating static linking).
-                // Also static libs are installed always on internal storage.
-                if (pkg.getStaticSharedLibName() != null) {
-                    Slog.w(TAG, "Cannot block uninstall of package: " + packageName
-                            + " providing static shared library: " + pkg.getStaticSharedLibName());
-                    return false;
-                }
+        PackageStateInternal packageState = getPackageStateInternal(packageName);
+        if (packageState != null && packageState.getPkg() != null) {
+            AndroidPackage pkg = packageState.getPkg();
+            // Cannot block uninstall SDK libs as they are controlled by SDK manager.
+            if (pkg.getSdkLibName() != null) {
+                Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+                        + " providing SDK library: " + pkg.getSdkLibName());
+                return false;
             }
-            mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
-            mSettings.writePackageRestrictionsLPr(userId);
+            // Cannot block uninstall of static shared libs as they are
+            // considered a part of the using app (emulating static linking).
+            // Also static libs are installed always on internal storage.
+            if (pkg.getStaticSharedLibName() != null) {
+                Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+                        + " providing static shared library: " + pkg.getStaticSharedLibName());
+                return false;
+            }
         }
+        synchronized (mLock) {
+            mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
+        }
+
+        scheduleWritePackageRestrictions(userId);
         return true;
     }
 
@@ -4978,24 +4925,17 @@
     }
 
     @Override
-    public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
+    public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) {
         PackageManagerServiceUtils.enforceSystemOrRoot(
                 "setRequiredForSystemUser can only be run by the system or root");
-        synchronized (mLock) {
-            PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps == null) {
-                Log.w(TAG, "Package doesn't exist: " + packageName);
-                return false;
-            }
-            if (systemUserApp) {
-                ps.setPrivateFlags(ps.getPrivateFlags()
-                        | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
-            } else {
-                ps.setPrivateFlags(ps.getPrivateFlags()
-                        & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
-            }
-            writeSettingsLPrTEMP();
+
+        PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+                packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser));
+        if (!result.isCommitted()) {
+            return false;
         }
+
+        scheduleWriteSettings();
         return true;
     }
 
@@ -5004,12 +4944,8 @@
         PackageManagerServiceUtils.enforceSystemOrRoot(
                 "Only the system can clear all profile data");
 
-        final AndroidPackage pkg;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-        }
-
-        try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
+        final AndroidPackage pkg = getPackage(packageName);
+        try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
             synchronized (mInstallLock) {
                 mAppDataHelper.clearAppProfilesLIF(pkg);
             }
@@ -5026,50 +4962,53 @@
         enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                 false /* checkShell */, "clear application data");
 
-        final boolean filterApp;
-        synchronized (mLock) {
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            filterApp = shouldFilterApplication(ps, callingUid, userId);
+        if (mComputer.getPackageStateFiltered(packageName, callingUid, userId) == null) {
+            if (observer != null) {
+                mHandler.post(() -> {
+                    try {
+                        observer.onRemoveCompleted(packageName, false);
+                    } catch (RemoteException e) {
+                        Log.i(TAG, "Observer no longer exists.");
+                    }
+                });
+            }
+            return;
         }
-        if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+        if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
             throw new SecurityException("Cannot clear data for a protected package: "
                     + packageName);
         }
+
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
                 final boolean succeeded;
-                if (!filterApp) {
-                    try (PackageFreezer freezer = freezePackage(packageName,
-                            "clearApplicationUserData")) {
-                        synchronized (mInstallLock) {
-                            succeeded = clearApplicationUserDataLIF(packageName, userId);
-                        }
-                        synchronized (mLock) {
-                            mInstantAppRegistry.deleteInstantApplicationMetadataLPw(
-                                    packageName, userId);
-                            if (succeeded) {
-                                resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
-                            }
+                try (PackageFreezer freezer = freezePackage(packageName,
+                        "clearApplicationUserData")) {
+                    synchronized (mInstallLock) {
+                        succeeded = clearApplicationUserDataLIF(packageName, userId);
+                    }
+                    mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId);
+                    synchronized (mLock) {
+                        if (succeeded) {
+                            resetComponentEnabledSettingsIfNeededLPw(packageName, userId);
                         }
                     }
-                    if (succeeded) {
-                        // invoke DeviceStorageMonitor's update method to clear any notifications
-                        DeviceStorageMonitorInternal dsm = LocalServices
-                                .getService(DeviceStorageMonitorInternal.class);
-                        if (dsm != null) {
-                            dsm.checkMemory();
-                        }
-                        if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
-                                == PERMISSION_GRANTED) {
-                            unsuspendForSuspendingPackage(packageName, userId);
-                            removeAllDistractingPackageRestrictions(userId);
-                            flushPackageRestrictionsAsUserInternalLocked(userId);
-                        }
+                }
+                if (succeeded) {
+                    // invoke DeviceStorageMonitor's update method to clear any notifications
+                    DeviceStorageMonitorInternal dsm = LocalServices
+                            .getService(DeviceStorageMonitorInternal.class);
+                    if (dsm != null) {
+                        dsm.checkMemory();
                     }
-                } else {
-                    succeeded = false;
+                    if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
+                            == PERMISSION_GRANTED) {
+                        unsuspendForSuspendingPackage(packageName, userId);
+                        removeAllDistractingPackageRestrictions(userId);
+                        flushPackageRestrictionsAsUserInternalLocked(userId);
+                    }
                 }
                 if (observer != null) {
                     try {
@@ -5089,17 +5028,7 @@
         }
 
         // Try finding details about the requested package
-        AndroidPackage pkg;
-        PackageSetting ps;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-            ps = mSettings.getPackageLPr(packageName);
-            if (pkg == null) {
-                if (ps != null) {
-                    pkg = ps.getPkg();
-                }
-            }
-        }
+        AndroidPackage pkg = getPackage(packageName);
         if (pkg == null) {
             Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
             return false;
@@ -5122,7 +5051,8 @@
         } else {
             flags = 0;
         }
-        mAppDataHelper.prepareAppDataContentsLIF(pkg, ps, userId, flags);
+        mAppDataHelper.prepareAppDataContentsLIF(pkg, getPackageStateInternal(packageName), userId,
+                flags);
 
         return true;
     }
@@ -5165,7 +5095,7 @@
 
         updateSequenceNumberLP(pkgSetting, new int[] { userId });
         updateInstantAppInstallerLocked(packageName);
-        scheduleWritePackageRestrictionsLocked(userId);
+        scheduleWritePackageRestrictions(userId);
 
         final ArrayList<String> pendingComponents = mPendingBroadcasts.get(userId, packageName);
         if (pendingComponents == null) {
@@ -5214,10 +5144,7 @@
         final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_INSTANT_APPS);
 
-        final AndroidPackage pkg;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-        }
+        final AndroidPackage pkg = getPackage(packageName);
 
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(() -> {
@@ -5438,8 +5365,8 @@
                 }
             }
             resolver.addFilter(newFilter);
-            scheduleWritePackageRestrictionsLocked(sourceUserId);
         }
+        scheduleWritePackageRestrictions(sourceUserId);
     }
 
     @Override
@@ -5460,8 +5387,8 @@
                     resolver.removeFilter(filter);
                 }
             }
-            scheduleWritePackageRestrictionsLocked(sourceUserId);
         }
+        scheduleWritePackageRestrictions(sourceUserId);
     }
 
     // Enforcing that callingUid is owning pkg on userId
@@ -5733,12 +5660,8 @@
     @Override
     public void setUpdateAvailable(String packageName, boolean updateAvailable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
-        synchronized (mLock) {
-            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
-            if (pkgSetting != null) {
-                pkgSetting.setUpdateAvailable(updateAvailable);
-            }
-        }
+        commitPackageStateMutation(null, packageName, state ->
+                state.setUpdateAvailable(updateAvailable));
     }
 
     @Override
@@ -5763,32 +5686,32 @@
         }
 
         int callingUid = Binder.getCallingUid();
-
         String componentPkgName = componentName.getPackageName();
-        int componentUid = getPackageUid(componentPkgName, 0, userId);
-        if (!UserHandle.isSameApp(callingUid, componentUid)) {
-            throw new SecurityException("The calling UID (" + callingUid + ")"
-                    + " does not match the target UID");
-        }
 
-        String allowedCallerPkg = mContext.getString(R.string.config_overrideComponentUiPackage);
-        if (TextUtils.isEmpty(allowedCallerPkg)) {
-            throw new SecurityException(
-                    "There is no package defined as allowed to change a component's label or icon");
-        }
+        boolean changed = executeWithConsistentComputerReturning(computer -> {
+            int componentUid = getPackageUid(componentPkgName, 0, userId);
+            if (!UserHandle.isSameApp(callingUid, componentUid)) {
+                throw new SecurityException("The calling UID (" + callingUid + ")"
+                        + " does not match the target UID");
+            }
 
-        int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
-                userId);
-        if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
-            throw new SecurityException("The calling UID (" + callingUid + ")"
-                    + " is not allowed to change a component's label or icon");
-        }
+            String allowedCallerPkg =
+                    mContext.getString(R.string.config_overrideComponentUiPackage);
+            if (TextUtils.isEmpty(allowedCallerPkg)) {
+                throw new SecurityException( "There is no package defined as allowed to change a "
+                        + "component's label or icon");
+            }
 
-        synchronized (mLock) {
-            AndroidPackage pkg = mPackages.get(componentPkgName);
-            PackageSetting pkgSetting = getPackageSettingForMutation(componentPkgName);
-            if (pkg == null || pkgSetting == null
-                    || (!pkg.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp())) {
+            int allowedCallerUid = getPackageUid(allowedCallerPkg, PackageManager.MATCH_SYSTEM_ONLY,
+                    userId);
+            if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
+                throw new SecurityException("The calling UID (" + callingUid + ")"
+                        + " is not allowed to change a component's label or icon");
+            }
+            PackageStateInternal packageState = computer.getPackageStateInternal(componentPkgName);
+            if (packageState == null || packageState.getPkg() == null
+                    || (!packageState.isSystem()
+                    && !packageState.getTransientState().isUpdatedSystemApp())) {
                 throw new SecurityException(
                         "Changing the label is not allowed for " + componentName);
             }
@@ -5797,13 +5720,23 @@
                 throw new IllegalArgumentException("Component " + componentName + " not found");
             }
 
-            if (!pkgSetting.overrideNonLocalizedLabelAndIcon(componentName, nonLocalizedLabel,
-                    icon, userId)) {
-                // Nothing changed
-                return;
-            }
+            Pair<String, Integer> overrideLabelIcon = packageState.getUserStateOrDefault(userId)
+                    .getOverrideLabelIconForComponent(componentName);
+
+            String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first;
+            Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second;
+
+            return !TextUtils.equals(existingLabel, nonLocalizedLabel)
+                    || !Objects.equals(existingIcon, icon);
+        });
+        if (!changed) {
+            return;
         }
 
+        commitPackageStateMutation(null, componentPkgName,
+                state -> state.userState(userId)
+                        .setComponentLabelIcon(componentName, nonLocalizedLabel, icon));
+
         ArrayList<String> components = mPendingBroadcasts.get(userId, componentPkgName);
         if (components == null) {
             components = new ArrayList<>();
@@ -6076,7 +6009,7 @@
             if (isSynchronous) {
                 flushPackageRestrictionsAsUserInternalLocked(userId);
             } else {
-                scheduleWritePackageRestrictionsLocked(userId);
+                scheduleWritePackageRestrictions(userId);
             }
             if (scheduleBroadcastMessage) {
                 if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
@@ -6187,9 +6120,11 @@
         // NOTE: this invokes synchronous disk access, so callers using this
         // method should consider running on a background thread
         mSettings.writePackageRestrictionsLPr(userId);
-        mDirtyUsers.remove(userId);
-        if (mDirtyUsers.isEmpty()) {
-            mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+        synchronized (mDirtyUsers) {
+            mDirtyUsers.remove(userId);
+            if (mDirtyUsers.isEmpty()) {
+                mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+            }
         }
     }
 
@@ -6216,29 +6151,50 @@
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
         if (!mUserManager.exists(userId)) return;
         final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return;
-        }
-        final int permission = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
-        final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
-        if (!allowedByPermission
-                && !ArrayUtils.contains(getPackagesForUid(callingUid), packageName)) {
-            throw new SecurityException(
-                    "Permission Denial: attempt to change stopped state from pid="
-                            + Binder.getCallingPid()
-                            + ", uid=" + callingUid + ", package=" + packageName);
-        }
-        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
-                true /* checkShell */, "stop package");
-        // writer
-        synchronized (mLock) {
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (!shouldFilterApplication(ps, callingUid, userId)
-                    && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
-                scheduleWritePackageRestrictionsLocked(userId);
+        Pair<Boolean, String> wasNotLaunchedAndInstallerPackageName =
+                executeWithConsistentComputerReturningThrowing(computer -> {
+            if (computer.getInstantAppPackageName(callingUid) != null) {
+                return null;
             }
+            final int permission = mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+            final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+            if (!allowedByPermission
+                    && !ArrayUtils.contains(computer.getPackagesForUid(callingUid), packageName)) {
+                throw new SecurityException(
+                        "Permission Denial: attempt to change stopped state from pid="
+                                + Binder.getCallingPid()
+                                + ", uid=" + callingUid + ", package=" + packageName);
+            }
+            computer.enforceCrossUserPermission(callingUid, userId,
+                    true /* requireFullPermission */, true /* checkShell */, "stop package");
+
+            final PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
+            final PackageUserState PackageUserState = packageState == null
+                    ? null : packageState.getUserStateOrDefault(userId);
+            if (packageState == null
+                    || computer.shouldFilterApplication(packageState, callingUid, userId)
+                    || PackageUserState.isStopped() == stopped) {
+                return null;
+            }
+
+            return Pair.create(PackageUserState.isNotLaunched(),
+                    packageState.getInstallSource().installerPackageName);
+        });
+        if (wasNotLaunchedAndInstallerPackageName != null) {
+            boolean wasNotLaunched = wasNotLaunchedAndInstallerPackageName.first;
+
+            commitPackageStateMutation(null, packageName, packageState -> {
+                PackageUserStateWrite userState = packageState.userState(userId);
+                userState.setStopped(stopped);
+                if (wasNotLaunched) {
+                    userState.setNotLaunched(false);
+                }
+            });
+
+            scheduleWritePackageRestrictions(userId);
         }
+
         // If this would cause the app to leave force-stop, then also make sure to unhibernate the
         // app if needed.
         if (!stopped) {
@@ -6490,19 +6446,17 @@
     }
 
     void dumpSnapshotStats(PrintWriter pw, boolean isBrief) {
-        if (!mSnapshotEnabled) {
-            pw.println("  Snapshots disabled");
-        } else {
-            int hits = 0;
-            int level = sSnapshotCorked.get();
-            synchronized (mSnapshotLock) {
-                if (mSnapshotComputer != null) {
-                    hits = mSnapshotComputer.getUsed();
-                }
-            }
-            final long now = SystemClock.currentTimeMicro();
-            mSnapshotStatistics.dump(pw, "  ", now, hits, level, isBrief);
+        if (mSnapshotStatistics == null) {
+            return;
         }
+        int hits = 0;
+        synchronized (mSnapshotLock) {
+            if (mSnapshotComputer != null) {
+                hits = mSnapshotComputer.getUsed();
+            }
+        }
+        final long now = SystemClock.currentTimeMicro();
+        mSnapshotStatistics.dump(pw, "  ", now, hits, -1, isBrief);
     }
 
     /**
@@ -6661,15 +6615,17 @@
     /** Called by UserManagerService */
     void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) {
         synchronized (mLock) {
-            mDirtyUsers.remove(userId);
+            synchronized (mDirtyUsers) {
+                mDirtyUsers.remove(userId);
+            }
             mUserNeedsBadging.delete(userId);
             mPermissionManager.onUserRemoved(userId);
             mSettings.removeUserLPw(userId);
             mPendingBroadcasts.remove(userId);
-            mInstantAppRegistry.onUserRemovedLPw(userId);
             mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId);
             mAppsFilter.onUserDeleted(userId);
         }
+        mInstantAppRegistry.onUserRemoved(userId);
     }
 
     /**
@@ -6688,7 +6644,7 @@
                     userTypeInstallablePackages, disallowedPackages);
         }
         synchronized (mLock) {
-            scheduleWritePackageRestrictionsLocked(userId);
+            scheduleWritePackageRestrictions(userId);
             scheduleWritePackageListLocked(userId);
             mAppsFilter.onUserCreated(userId);
         }
@@ -6796,20 +6752,23 @@
         return mComputer.isPackageSignedByKeySetExactly(packageName, ks);
     }
 
-    @GuardedBy("mLock")
-    private void deletePackageIfUnusedLPr(final String packageName) {
-        PackageSetting ps = mSettings.getPackageLPr(packageName);
+    private void deletePackageIfUnused(final String packageName) {
+        PackageStateInternal ps = getPackageStateInternal(packageName);
         if (ps == null) {
             return;
         }
-        if (!ps.isAnyInstalled(mUserManager.getUserIds())) {
-            // TODO Implement atomic delete if package is unused
-            // It is currently possible that the package will be deleted even if it is installed
-            // after this method returns.
-            mHandler.post(() -> mDeletePackageHelper.deletePackageX(
-                    packageName, PackageManager.VERSION_CODE_HIGHEST,
-                    0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
+        final SparseArray<? extends PackageUserStateInternal> userStates = ps.getUserStates();
+        for (int index = 0; index < userStates.size(); index++) {
+            if (userStates.valueAt(index).isInstalled()) {
+                return;
+            }
         }
+        // TODO Implement atomic delete if package is unused
+        // It is currently possible that the package will be deleted even if it is installed
+        // after this method returns.
+        mHandler.post(() -> mDeletePackageHelper.deletePackageX(
+                packageName, PackageManager.VERSION_CODE_HIGHEST,
+                0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
     }
 
     private AndroidPackage getPackage(String packageName) {
@@ -6985,7 +6944,7 @@
         @Override
         public PackageList getPackageList(@Nullable PackageListObserver observer) {
             final ArrayList<String> list = new ArrayList<>();
-            forEachPackageState(false, packageState -> {
+            forEachPackageState(packageState -> {
                 AndroidPackage pkg = packageState.getPkg();
                 if (pkg != null) {
                     list.add(pkg.getPackageName());
@@ -6993,18 +6952,14 @@
             });
             final PackageList packageList = new PackageList(list, observer);
             if (observer != null) {
-                synchronized (mLock) {
-                    mPackageListObservers.add(packageList);
-                }
+                mPackageObserverHelper.addObserver(packageList);
             }
             return packageList;
         }
 
         @Override
         public void removePackageListObserver(PackageListObserver observer) {
-            synchronized (mLock) {
-                mPackageListObservers.remove(observer);
-            }
+            mPackageObserverHelper.removeObserver(observer);
         }
 
         @Override
@@ -7203,6 +7158,16 @@
                 SparseArray<String> profileOwnerPackages) {
             mProtectedPackages.setDeviceAndProfileOwnerPackages(
                     deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
+            final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
+            if (deviceOwnerPackage != null) {
+                usersWithPoOrDo.add(deviceOwnerUserId);
+            }
+            final int sz = profileOwnerPackages.size();
+            for (int i = 0; i < sz; i++) {
+                if (profileOwnerPackages.valueAt(i) != null) {
+                    removeAllNonSystemPackageSuspensions(profileOwnerPackages.keyAt(i));
+                }
+            }
         }
 
         @Override
@@ -7276,32 +7241,32 @@
         @Override
         public void grantImplicitAccess(int userId, Intent intent,
                 int recipientAppId, int visibleUid, boolean direct, boolean retainOnUpdate) {
-            synchronized (mLock) {
-                final AndroidPackage visiblePackage = getPackage(visibleUid);
+            boolean accessGranted = executeWithConsistentComputerReturning(computer -> {
+                final AndroidPackage visiblePackage = computer.getPackage(visibleUid);
                 final int recipientUid = UserHandle.getUid(userId, recipientAppId);
-                if (visiblePackage == null || getPackage(recipientUid) == null) {
-                    return;
+                if (visiblePackage == null || computer.getPackage(recipientUid) == null) {
+                    return false;
                 }
 
-                final boolean instantApp =
-                        isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
-                final boolean accessGranted;
+                final boolean instantApp = computer.isInstantAppInternal(
+                        visiblePackage.getPackageName(), userId, visibleUid);
                 if (instantApp) {
                     if (!direct) {
                         // if the interaction that lead to this granting access to an instant app
                         // was indirect (i.e.: URI permission grant), do not actually execute the
                         // grant.
-                        return;
+                        return false;
                     }
-                    accessGranted = mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+                    return mInstantAppRegistry.grantInstantAccess(userId, intent,
                             recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
                 } else {
-                    accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
+                    return mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
                             retainOnUpdate);
                 }
-                if (accessGranted) {
-                    ApplicationPackageManager.invalidateGetPackagesForUidCache();
-                }
+            });
+
+            if (accessGranted) {
+                ApplicationPackageManager.invalidateGetPackagesForUidCache();
             }
         }
 
@@ -7314,7 +7279,8 @@
 
         @Override
         public void pruneInstantApps() {
-            mInstantAppRegistry.pruneInstantApps();
+            executeWithConsistentComputer(computer ->
+                    mInstantAppRegistry.pruneInstantApps(computer));
         }
 
         @Override
@@ -7361,7 +7327,7 @@
         @Override
         public List<PackageInfo> getOverlayPackages(int userId) {
             final ArrayList<PackageInfo> overlayPackages = new ArrayList<>();
-            forEachPackageState(false, packageState -> {
+            forEachPackageState(packageState -> {
                 final AndroidPackage pkg = packageState.getPkg();
                 if (pkg != null && pkg.getOverlayTarget() != null) {
                     PackageInfo pkgInfo = generatePackageInfo(packageState, 0, userId);
@@ -7377,7 +7343,7 @@
         @Override
         public List<String> getTargetPackageNames(int userId) {
             List<String> targetPackages = new ArrayList<>();
-            forEachPackageState(false, packageState -> {
+            forEachPackageState(packageState -> {
                 final AndroidPackage pkg = packageState.getPkg();
                 if (pkg != null && !pkg.isOverlay()) {
                     targetPackages.add(pkg.getPackageName());
@@ -7390,53 +7356,8 @@
         public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
                 @Nullable OverlayPaths overlayPaths,
                 @NonNull Set<String> outUpdatedPackageNames) {
-            boolean modified = false;
-            synchronized (mLock) {
-                final AndroidPackage targetPkg = mPackages.get(targetPackageName);
-                if (targetPackageName == null || targetPkg == null) {
-                    Slog.e(TAG, "failed to find package " + targetPackageName);
-                    return false;
-                }
-
-                if (targetPkg.getLibraryNames() != null) {
-                    // Set the overlay paths for dependencies of the shared library.
-                    for (final String libName : targetPkg.getLibraryNames()) {
-                        final SharedLibraryInfo info = getSharedLibraryInfo(libName,
-                                SharedLibraryInfo.VERSION_UNDEFINED);
-                        if (info == null) {
-                            continue;
-                        }
-                        final List<VersionedPackage> dependents = getPackagesUsingSharedLibrary(
-                                info, 0, Process.SYSTEM_UID, userId);
-                        if (dependents == null) {
-                            continue;
-                        }
-                        for (final VersionedPackage dependent : dependents) {
-                            final PackageSetting ps = mSettings.getPackageLPr(
-                                    dependent.getPackageName());
-                            if (ps == null) {
-                                continue;
-                            }
-                            if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) {
-                                outUpdatedPackageNames.add(dependent.getPackageName());
-                                modified = true;
-                            }
-                        }
-                    }
-                }
-
-                final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
-                if (ps.setOverlayPaths(overlayPaths, userId)) {
-                    outUpdatedPackageNames.add(targetPackageName);
-                    modified = true;
-                }
-
-                if (modified) {
-                    invalidatePackageInfoCache();
-                }
-            }
-
-            return true;
+            return PackageManagerService.this.setEnabledOverlayPackages(userId, targetPackageName,
+                    overlayPaths, outUpdatedPackageNames);
         }
 
         @Override
@@ -7504,15 +7425,13 @@
 
         @Override
         public boolean hasInstantApplicationMetadata(String packageName, int userId) {
-            synchronized (mLock) {
-                return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId);
-            }
+            return mInstantAppRegistry.hasInstantApplicationMetadata(packageName, userId);
         }
 
         @Override
         public void notifyPackageUse(String packageName, int reason) {
             synchronized (mLock) {
-                PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
+                PackageManagerService.this.notifyPackageUseInternal(packageName, reason);
             }
         }
 
@@ -7549,30 +7468,24 @@
         }
 
         @Override
-        public void forEachPackage(Consumer<AndroidPackage> actionLocked) {
-            PackageManagerService.this.forEachPackage(actionLocked);
-        }
-
-        @Override
         public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
             PackageManagerService.this.forEachPackageSetting(actionLocked);
         }
 
         @Override
-        public void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
-            PackageManagerService.this.forEachPackageState(locked, action);
+        public void forEachPackageState(Consumer<PackageStateInternal> action) {
+            PackageManagerService.this.forEachPackageState(action);
         }
 
         @Override
-        public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
+        public void forEachPackage(Consumer<AndroidPackage> action) {
+            PackageManagerService.this.forEachPackage(action);
+        }
+
+        @Override
+        public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> action,
                 @UserIdInt int userId) {
-            forEachInstalledPackage(true, actionLocked, userId);
-        }
-
-        @Override
-        public void forEachInstalledPackage(boolean locked,
-                @NonNull Consumer<AndroidPackage> action, int userId) {
-            PackageManagerService.this.forEachInstalledPackage(locked, action, userId);
+            PackageManagerService.this.forEachInstalledPackage(action, userId);
         }
 
         @Override
@@ -7695,9 +7608,7 @@
 
         @Override
         public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
-            synchronized (mLock) {
-                mSettings.updateRuntimePermissionsFingerprintLPr(userId);
-            }
+            mSettings.updateRuntimePermissionsFingerprint(userId);
         }
 
         @Override
@@ -7736,9 +7647,7 @@
 
         @Override
         public boolean isPermissionUpgradeNeeded(int userId) {
-            synchronized (mLock) {
-                return mSettings.isPermissionUpgradeNeededLPr(userId);
-            }
+            return mSettings.isPermissionUpgradeNeeded(userId);
         }
 
         @Override
@@ -7910,14 +7819,104 @@
         }
     }
 
+    private boolean setEnabledOverlayPackages(@UserIdInt int userId,
+            @NonNull String targetPackageName, @Nullable OverlayPaths newOverlayPaths,
+            @NonNull Set<String> outUpdatedPackageNames) {
+        synchronized (mOverlayPathsLock) {
+            final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents = new ArrayMap<>();
+            Boolean targetModified = executeWithConsistentComputerReturning(computer -> {
+                final PackageStateInternal packageState = computer.getPackageStateInternal(
+                        targetPackageName);
+                final AndroidPackage targetPkg =
+                        packageState == null ? null : packageState.getPkg();
+                if (targetPackageName == null || targetPkg == null) {
+                    Slog.e(TAG, "failed to find package " + targetPackageName);
+                    return null;
+                }
+
+                if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+                        newOverlayPaths)) {
+                    return false;
+                }
+
+                if (targetPkg.getLibraryNames() != null) {
+                    // Set the overlay paths for dependencies of the shared library.
+                    for (final String libName : targetPkg.getLibraryNames()) {
+                        ArraySet<String> modifiedDependents = null;
+
+                        final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
+                                SharedLibraryInfo.VERSION_UNDEFINED);
+                        if (info == null) {
+                            continue;
+                        }
+                        final List<VersionedPackage> dependents = computer
+                                .getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, userId);
+                        if (dependents == null) {
+                            continue;
+                        }
+                        for (final VersionedPackage dependent : dependents) {
+                            final PackageStateInternal dependentState =
+                                    computer.getPackageStateInternal(dependent.getPackageName());
+                            if (dependentState == null) {
+                                continue;
+                            }
+                            if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
+                                    .getSharedLibraryOverlayPaths()
+                                    .get(libName), newOverlayPaths)) {
+                                String dependentPackageName = dependent.getPackageName();
+                                modifiedDependents = ArrayUtils.add(modifiedDependents,
+                                        dependentPackageName);
+                                outUpdatedPackageNames.add(dependentPackageName);
+                            }
+                        }
+
+                        if (modifiedDependents != null) {
+                            libNameToModifiedDependents.put(libName, modifiedDependents);
+                        }
+                    }
+                }
+
+                outUpdatedPackageNames.add(targetPackageName);
+                return true;
+            });
+
+            if (targetModified == null) {
+                // Null indicates error
+                return false;
+            } else if (!targetModified) {
+                // Treat non-modification as a successful commit
+                return true;
+            }
+
+            commitPackageStateMutation(null, mutator -> {
+                mutator.forPackage(targetPackageName)
+                        .userState(userId)
+                        .setOverlayPaths(newOverlayPaths);
+
+                for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size(); mapIndex++) {
+                    String libName = libNameToModifiedDependents.keyAt(mapIndex);
+                    ArraySet<String> modifiedDependents =
+                            libNameToModifiedDependents.valueAt(mapIndex);
+                    for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
+                        mutator.forPackage(modifiedDependents.valueAt(setIndex))
+                                .userState(userId)
+                                .setOverlayPathsForLibrary(libName, newOverlayPaths);
+                    }
+                }
+            });
+        }
+
+        invalidatePackageInfoCache();
+
+        return true;
+    }
+
     @Override
     public int getRuntimePermissionsVersion(@UserIdInt int userId) {
         Preconditions.checkArgumentNonnegative(userId);
         enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
                 "getRuntimePermissionVersion");
-        synchronized (mLock) {
-            return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
-        }
+        return mSettings.getDefaultRuntimePermissionsVersion(userId);
     }
 
     @Override
@@ -7926,9 +7925,7 @@
         Preconditions.checkArgumentNonnegative(userId);
         enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
                 "setRuntimePermissionVersion");
-        synchronized (mLock) {
-            mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
-        }
+        mSettings.setDefaultRuntimePermissionsVersion(version, userId);
     }
 
     private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
@@ -7964,58 +7961,19 @@
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @Nullable
     PackageStateInternal getPackageStateInternal(String packageName) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                PackageSetting pkgSetting =
-                        (PackageSetting) computer.getPackageStateInternal(packageName);
-                if (pkgSetting == null) {
-                    return null;
-                }
-
-                return new PackageSetting(pkgSetting);
-            }
-        } else {
-            return computer.getPackageStateInternal(packageName);
-        }
+        return mComputer.getPackageStateInternal(packageName);
     }
 
     @Nullable
     PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                PackageSetting pkgSetting =
-                        (PackageSetting) computer.getPackageStateInternal(packageName, callingUid);
-                if (pkgSetting == null) {
-                    return null;
-                }
-
-                return new PackageSetting(pkgSetting);
-            }
-        } else {
-            return computer.getPackageStateInternal(packageName, callingUid);
-        }
+        return mComputer.getPackageStateInternal(packageName, callingUid);
     }
 
     @Nullable
     PackageStateInternal getPackageStateInstalledFiltered(@NonNull String packageName,
             int callingUid, @UserIdInt int userId) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                PackageSetting pkgSetting =
-                        (PackageSetting) filterPackageStateForInstalledAndFiltered(computer,
-                                packageName, callingUid, userId);
-                if (pkgSetting == null) {
-                    return null;
-                }
-                return new PackageSetting(pkgSetting);
-            }
-        } else {
-            return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid,
-                    userId);
-        }
+        return filterPackageStateForInstalledAndFiltered(mComputer, packageName, callingUid,
+                userId);
     }
 
     @Nullable
@@ -8057,31 +8015,19 @@
         }
     }
 
-    void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
-        if (locked) {
-            synchronized (mLiveComputerSyncLock) {
-                forEachPackageState(mComputer.getPackageStates(), action);
-            }
-        } else {
-            Computer computer = snapshotComputer();
-            if (computer == mLiveComputer) {
-                synchronized (mLiveComputerSyncLock) {
-                    forEachPackageState(computer.getPackageStates(), action);
-                }
-            } else {
-                forEachPackageState(computer.getPackageStates(), action);
-            }
-        }
+    void forEachPackageState(Consumer<PackageStateInternal> consumer) {
+        forEachPackageState(mComputer.getPackageStates(), consumer);
     }
 
-    void forEachPackage(Consumer<AndroidPackage> action) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                forEachPackage(computer.getPackageStates(), action);
+    void forEachPackage(Consumer<AndroidPackage> consumer) {
+        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
+                mComputer.getPackageStates();
+        int size = packageStates.size();
+        for (int index = 0; index < size; index++) {
+            PackageStateInternal packageState = packageStates.valueAt(index);
+            if (packageState.getPkg() != null) {
+                consumer.accept(packageState.getPkg());
             }
-        } else {
-            forEachPackage(computer.getPackageStates(), action);
         }
     }
 
@@ -8095,19 +8041,7 @@
         }
     }
 
-    private void forEachPackage(
-            @NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
-            @NonNull Consumer<AndroidPackage> consumer) {
-        int size = packageStates.size();
-        for (int index = 0; index < size; index++) {
-            PackageStateInternal packageState = packageStates.valueAt(index);
-            if (packageState.getPkg() != null) {
-                consumer.accept(packageState.getPkg());
-            }
-        }
-    }
-
-    void forEachInstalledPackage(boolean locked, @NonNull Consumer<AndroidPackage> action,
+    void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> action,
             @UserIdInt int userId) {
         Consumer<PackageStateInternal> actionWrapped = packageState -> {
             if (packageState.getPkg() != null
@@ -8115,84 +8049,37 @@
                 action.accept(packageState.getPkg());
             }
         };
-        if (locked) {
-            synchronized (mLiveComputerSyncLock) {
-                forEachPackageState(mComputer.getPackageStates(), actionWrapped);
-            }
-        } else {
-            Computer computer = snapshotComputer();
-            if (computer == mLiveComputer) {
-                synchronized (mLiveComputerSyncLock) {
-                    forEachPackageState(computer.getPackageStates(), actionWrapped);
-                }
-            } else {
-                forEachPackageState(computer.getPackageStates(), actionWrapped);
-            }
-        }
+        forEachPackageState(mComputer.getPackageStates(), actionWrapped);
     }
 
-    private void executeWithConsistentComputer(
+    // TODO: Make private
+    void executeWithConsistentComputer(
             @NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                consumer.accept(computer);
-            }
-        } else {
-            consumer.accept(computer);
-        }
+        consumer.accept(snapshotComputer());
     }
 
     private <T> T executeWithConsistentComputerReturning(
             @NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                return function.apply(computer);
-            }
-        } else {
-            return function.apply(computer);
-        }
+        return function.apply(snapshotComputer());
     }
 
     private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
             @NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
             throws ExceptionType {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                consumer.accept(computer);
-            }
-        } else {
-            consumer.accept(computer);
-        }
+        consumer.accept(snapshotComputer());
     }
 
     private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
     executeWithConsistentComputerThrowing2(
             @NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
                     ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                consumer.accept(computer);
-            }
-        } else {
-            consumer.accept(computer);
-        }
+        consumer.accept(snapshotComputer());
     }
 
     private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
             @NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
             throws ExceptionType {
-        Computer computer = snapshotComputer();
-        if (computer == mLiveComputer) {
-            synchronized (mLiveComputerSyncLock) {
-                return function.apply(computer);
-            }
-        } else {
-            return function.apply(computer);
-        }
+        return function.apply(snapshotComputer());
     }
 
     boolean isHistoricalPackageUsageAvailable() {
@@ -8276,9 +8163,7 @@
         if (!isInstantApp(packageName, userId)) {
             return null;
         }
-        synchronized (mLock) {
-            return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
-        }
+        return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId);
     }
 
     @Override
@@ -8333,10 +8218,13 @@
                     + SET_HARMFUL_APP_WARNINGS + " permission.");
         }
 
-        synchronized (mLock) {
-            mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
-            scheduleWritePackageRestrictionsLocked(userId);
+        PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
+                packageState -> packageState.userState(userId)
+                        .setHarmfulAppWarning(warning == null ? null : warning.toString()));
+        if (result.isSpecificPackageNull()) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
         }
+        scheduleWritePackageRestrictions(userId);
     }
 
     @Nullable
@@ -8388,14 +8276,23 @@
     @Override
     public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
         enforceOwnerRights(packageName, Binder.getCallingUid());
-        final boolean changed;
-        synchronized (mLock) {
-            changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup,
-                    new ArraySet<>(mimeTypes));
+        mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
+        final PackageStateInternal packageState = getPackageStateInternal(packageName);
+        Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
+        if (existingMimeTypes == null) {
+            throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+                    + " for package " + packageName);
         }
-        if (changed) {
-            applyMimeGroupChanges(packageName, mimeGroup);
+        if (existingMimeTypes.size() == mimeTypes.size()
+                && existingMimeTypes.containsAll(mimeTypes)) {
+            return;
         }
+
+        ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
+        commitPackageStateMutation(null, packageName, packageStateWrite -> {
+            packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
+        });
+        applyMimeGroupChanges(packageName, mimeGroup);
     }
 
     @Override
@@ -8426,8 +8323,15 @@
         enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                 false /* checkShell */, "setSplashScreenTheme");
         enforceOwnerRights(packageName, callingUid);
-        mutateInstalledPackageSetting(packageName, callingUid, userId,
-                pkgSetting -> pkgSetting.setSplashScreenTheme(userId, themeId));
+
+        PackageStateInternal packageState = getPackageStateInstalledFiltered(packageName,
+                callingUid, userId);
+        if (packageState == null) {
+            return;
+        }
+
+        commitPackageStateMutation(null, packageName, state ->
+                state.userState(userId).setSplashScreenTheme(themeId));
     }
 
     @Override
@@ -8442,6 +8346,7 @@
      * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
      * writeLegacyPermissionsTEMP() beforehand.
      *
+     * TODO: In the meantime, can this be moved to a schedule call?
      * TODO(b/182523293): This should be removed once we finish migration of permission storage.
      */
     void writeSettingsLPrTEMP() {
@@ -8532,57 +8437,54 @@
         }
 
         final int[] allUsers = mInjector.getUserManagerService().getUserIds();
+        final List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
+        for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
+            final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
+            final PackageStateInternal ps = getPackageStateInternal(perPackage.packageName);
+            if (ps == null) {
+                if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                    Slog.i(TAG, "PerUidReadTimeouts: package not found = "
+                            + perPackage.packageName);
+                }
+                continue;
+            }
+            if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
+                if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                    Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
+                            + ps.getAppId());
+                }
+                continue;
+            }
 
-        List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
-        synchronized (mLock) {
-            for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
-                final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
-                final PackageSetting ps = mSettings.mPackages.get(perPackage.packageName);
-                if (ps == null) {
-                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
-                        Slog.i(TAG, "PerUidReadTimeouts: package not found = "
-                                + perPackage.packageName);
-                    }
+            final AndroidPackage pkg = ps.getPkg();
+            if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
+                    || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
+                if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                    Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
+                            + perPackage.packageName + ":" + pkg.getLongVersionCode());
+                }
+                continue;
+            }
+            if (perPackage.sha256certificate != null
+                    && !pkg.getSigningDetails().hasSha256Certificate(
+                    perPackage.sha256certificate)) {
+                if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                    Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
+                            + perPackage.packageName + ":" + pkg.getLongVersionCode());
+                }
+                continue;
+            }
+            for (int userId : allUsers) {
+                if (!ps.getUserStateOrDefault(userId).isInstalled()) {
                     continue;
                 }
-                if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
-                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
-                        Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
-                                + ps.getAppId());
-                    }
-                    continue;
-                }
-
-                final AndroidPackage pkg = ps.getPkg();
-                if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
-                        || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
-                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
-                        Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
-                                + perPackage.packageName + ":" + pkg.getLongVersionCode());
-                    }
-                    continue;
-                }
-                if (perPackage.sha256certificate != null
-                        && !pkg.getSigningDetails().hasSha256Certificate(
-                        perPackage.sha256certificate)) {
-                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
-                        Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
-                                + perPackage.packageName + ":" + pkg.getLongVersionCode());
-                    }
-                    continue;
-                }
-                for (int userId : allUsers) {
-                    if (!ps.getInstalled(userId)) {
-                        continue;
-                    }
-                    final int uid = UserHandle.getUid(userId, ps.getAppId());
-                    final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
-                    perUid.uid = uid;
-                    perUid.minTimeUs = perPackage.timeouts.minTimeUs;
-                    perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
-                    perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
-                    result.add(perUid);
-                }
+                final int uid = UserHandle.getUid(userId, ps.getAppId());
+                final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
+                perUid.uid = uid;
+                perUid.minTimeUs = perPackage.timeouts.minTimeUs;
+                perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
+                perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
+                result.add(perUid);
             }
         }
         return result.toArray(new PerUidReadTimeouts[result.size()]);
@@ -8600,33 +8502,23 @@
 
     private void setKeepUninstalledPackagesInternal(List<String> packageList) {
         Preconditions.checkNotNull(packageList);
-        List<String> removedFromList = null;
-        synchronized (mLock) {
-            if (mKeepUninstalledPackages != null) {
-                final int packagesCount = mKeepUninstalledPackages.size();
-                for (int i = 0; i < packagesCount; i++) {
-                    String oldPackage = mKeepUninstalledPackages.get(i);
-                    if (packageList != null && packageList.contains(oldPackage)) {
-                        continue;
-                    }
-                    if (removedFromList == null) {
-                        removedFromList = new ArrayList<>();
-                    }
-                    removedFromList.add(oldPackage);
-                }
-            }
-            mKeepUninstalledPackages = new ArrayList<>(packageList);
-            if (removedFromList != null) {
-                final int removedCount = removedFromList.size();
-                for (int i = 0; i < removedCount; i++) {
-                    deletePackageIfUnusedLPr(removedFromList.get(i));
-                }
+        synchronized (mKeepUninstalledPackages) {
+            List<String> toRemove = new ArrayList<>(mKeepUninstalledPackages);
+            toRemove.removeAll(packageList); // Do not remove anything still in the list
+
+            mKeepUninstalledPackages.clear();
+            mKeepUninstalledPackages.addAll(packageList);
+
+            for (int i = 0; i < toRemove.size(); i++) {
+                deletePackageIfUnused(toRemove.get(i));
             }
         }
     }
 
     boolean shouldKeepUninstalledPackageLPr(String packageName) {
-        return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
+        synchronized (mKeepUninstalledPackages) {
+            return mKeepUninstalledPackages.contains(packageName);
+        }
     }
 
     @Override
@@ -8904,7 +8796,7 @@
      */
     @NonNull
     public PackageStateMutator.InitialState recordInitialState() {
-        return mPackageStateMutator.initialState(mChangedPackagesSequenceNumber);
+        return mPackageStateMutator.initialState(mChangedPackagesTracker.getSequenceNumber());
     }
 
     /**
@@ -8917,7 +8809,7 @@
             @NonNull Consumer<PackageStateMutator> consumer) {
         synchronized (mPackageStateWriteLock) {
             final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
-                    initialState, mChangedPackagesSequenceNumber);
+                    initialState, mChangedPackagesTracker.getSequenceNumber());
             if (result != PackageStateMutator.Result.SUCCESS) {
                 return result;
             }
@@ -8939,7 +8831,7 @@
             @NonNull Consumer<PackageStateWrite> consumer) {
         synchronized (mPackageStateWriteLock) {
             final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
-                    initialState, mChangedPackagesSequenceNumber);
+                    initialState, mChangedPackagesTracker.getSequenceNumber());
             if (result != PackageStateMutator.Result.SUCCESS) {
                 return result;
             }
@@ -8951,9 +8843,14 @@
                 consumer.accept(state);
             }
 
-            onChanged();
+            state.onChanged();
         }
 
         return PackageStateMutator.Result.SUCCESS;
     }
+
+    void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
+        executeWithConsistentComputer(computer ->
+                mInstantAppRegistry.onPackageInstalled(computer, packageName, newUsers));
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index db60686..1caa76d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -57,6 +57,7 @@
     public IncrementalManager incrementalManager;
     public PackageInstallerService installerService;
     public InstantAppRegistry instantAppRegistry;
+    public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
     public InstantAppResolverConnection instantAppResolverConnection;
     public ComponentName instantAppResolverSettingsComponent;
     public boolean isPreNmr1Upgrade;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index dcc4386..19c31e0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -463,18 +463,18 @@
      * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
      * and is not tamperproof.
      */
-    private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
-            PackageSetting disabledPkgSetting) {
-        if (pkgSetting.getSigningDetails().checkCapability(
+    private static boolean matchSignatureInSystem(@NonNull String packageName,
+            @NonNull SigningDetails signingDetails, PackageSetting disabledPkgSetting) {
+        if (signingDetails.checkCapability(
                 disabledPkgSetting.getSigningDetails(),
                 SigningDetails.CertCapabilities.INSTALLED_DATA)
                 || disabledPkgSetting.getSigningDetails().checkCapability(
-                pkgSetting.getSigningDetails(),
+                signingDetails,
                 SigningDetails.CertCapabilities.ROLLBACK)) {
             return true;
         } else {
             logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
-                    pkgSetting.getPackageName());
+                    packageName);
             return false;
         }
     }
@@ -536,7 +536,8 @@
             }
 
             if (!match && isApkVerificationForced(disabledPkgSetting)) {
-                match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
+                match = matchSignatureInSystem(packageName, pkgSetting.getSigningDetails(),
+                        disabledPkgSetting);
             }
 
             if (!match && isRollback) {
diff --git a/services/core/java/com/android/server/pm/PackageObserverHelper.java b/services/core/java/com/android/server/pm/PackageObserverHelper.java
new file mode 100644
index 0000000..ec42f2e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageObserverHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManagerInternal.PackageListObserver;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+class PackageObserverHelper {
+
+    @NonNull
+    private final Object mLock = new Object();
+
+    // True set of observers, immutable, used to iterate without blocking the lock, since
+    // callbacks can take a long time to return. The previous alternative used a bunch of
+    // list copies on each notify call, which is suboptimal in cases of few mutations and
+    // lots of notifications.
+    @NonNull
+    @GuardedBy("mLock")
+    private ArraySet<PackageListObserver> mActiveSnapshot = new ArraySet<>();
+
+    public void addObserver(@NonNull PackageListObserver observer) {
+        synchronized (mLock) {
+            ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
+            set.add(observer);
+            mActiveSnapshot = set;
+        }
+    }
+
+    public void removeObserver(@NonNull PackageListObserver observer) {
+        synchronized (mLock) {
+            ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
+            set.remove(observer);
+            mActiveSnapshot = set;
+        }
+    }
+
+    public void notifyAdded(@NonNull String packageName, int uid) {
+        ArraySet<PackageListObserver> observers;
+        synchronized (mLock) {
+            observers = mActiveSnapshot;
+        }
+        final int size = observers.size();
+        for (int index = 0; index < size; index++) {
+            observers.valueAt(index).onPackageAdded(packageName, uid);
+        }
+    }
+
+    public void notifyChanged(@NonNull String packageName, int uid) {
+        ArraySet<PackageListObserver> observers;
+        synchronized (mLock) {
+            observers = mActiveSnapshot;
+        }
+        final int size = observers.size();
+        for (int index = 0; index < size; index++) {
+            observers.valueAt(index).onPackageChanged(packageName, uid);
+        }
+    }
+
+    public void notifyRemoved(@NonNull String packageName, int uid) {
+        ArraySet<PackageListObserver> observers;
+        synchronized (mLock) {
+            observers = mActiveSnapshot;
+        }
+        final int size = observers.size();
+        for (int index = 0; index < size; index++) {
+            observers.valueAt(index).onPackageRemoved(packageName, uid);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 5fc840c..d2abc69 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -68,10 +68,10 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.Predicate;
 
 /**
  * Settings data for a particular package we know about.
+ *
  * @hide
  */
 @DataClass(genGetters = true, genConstructor = false, genSetters = false, genBuilder = false)
@@ -189,7 +189,7 @@
     private boolean forceQueryableOverride;
 
     @NonNull
-    private PackageStateUnserialized pkgState = new PackageStateUnserialized();
+    private final PackageStateUnserialized pkgState = new PackageStateUnserialized(this);
 
     @NonNull
     private UUID mDomainSetId;
@@ -722,10 +722,6 @@
         return readUserState(userId).getEnabledState();
     }
 
-    String getLastDisabledAppCaller(int userId) {
-        return readUserState(userId).getLastDisableAppCaller();
-    }
-
     void setInstalled(boolean inst, int userId) {
         modifyUserState(userId).setInstalled(inst);
         onChanged();
@@ -753,14 +749,6 @@
         onChanged();
     }
 
-    boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
-        boolean changed = modifyUserState(userId).setOverlayPaths(overlayPaths);
-        if (changed) {
-            onChanged();
-        }
-        return changed;
-    }
-
     @NonNull
     OverlayPaths getOverlayPaths(int userId) {
         return readUserState(userId).getOverlayPaths();
@@ -773,11 +761,6 @@
         return changed;
     }
 
-    @NonNull
-    Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
-        return readUserState(userId).getSharedLibraryOverlayPaths();
-    }
-
     boolean isAnyInstalled(int[] users) {
         for (int user: users) {
             if (readUserState(user).isInstalled()) {
@@ -850,55 +833,6 @@
         onChanged();
     }
 
-    boolean getSuspended(int userId) {
-        return readUserState(userId).isSuspended();
-    }
-
-    boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
-            PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
-        final PackageUserStateImpl existingUserState = modifyUserState(userId);
-        final SuspendParams newSuspendParams = SuspendParams.getInstanceOrNull(dialogInfo,
-                appExtras, launcherExtras);
-        if (existingUserState.getSuspendParams() == null) {
-            existingUserState.setSuspendParams(new ArrayMap<>());
-        }
-        final SuspendParams oldSuspendParams =
-                existingUserState.getSuspendParams().put(suspendingPackage, newSuspendParams);
-        onChanged();
-        return !Objects.equals(oldSuspendParams, newSuspendParams);
-    }
-
-    boolean removeSuspension(String suspendingPackage, int userId) {
-        boolean wasModified = false;
-        final PackageUserStateImpl existingUserState = modifyUserState(userId);
-        if (existingUserState.getSuspendParams() != null) {
-            if (existingUserState.getSuspendParams().remove(suspendingPackage) != null) {
-                wasModified = true;
-            }
-            if (existingUserState.getSuspendParams().size() == 0) {
-                existingUserState.setSuspendParams(null);
-            }
-        }
-        onChanged();
-        return wasModified;
-    }
-
-    void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
-        final PackageUserStateImpl existingUserState = modifyUserState(userId);
-        if (existingUserState.getSuspendParams() != null) {
-            for (int i = existingUserState.getSuspendParams().size() - 1; i >= 0; i--) {
-                final String suspendingPackage = existingUserState.getSuspendParams().keyAt(i);
-                if (suspendingPackagePredicate.test(suspendingPackage)) {
-                    existingUserState.getSuspendParams().removeAt(i);
-                }
-            }
-            if (existingUserState.getSuspendParams().size() == 0) {
-                existingUserState.setSuspendParams(null);
-            }
-        }
-        onChanged();
-    }
-
     public boolean getInstantApp(int userId) {
         return readUserState(userId).isInstantApp();
     }
@@ -1095,8 +1029,8 @@
     }
 
     /**
-     * TODO (b/170263003) refactor to dump to permissiongr proto
-     * Dumps the permissions that are granted to users for this package.
+     * TODO (b/170263003) refactor to dump to permissiongr proto Dumps the permissions that are
+     * granted to users for this package.
      */
     void writePackageUserPermissionsProto(ProtoOutputStream proto, long fieldId,
             List<UserInfo> users, LegacyPermissionDataProvider dataProvider) {
@@ -1154,16 +1088,6 @@
         }
     }
 
-    void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
-        modifyUserState(userId).setHarmfulAppWarning(harmfulAppWarning);
-        onChanged();
-    }
-
-    String getHarmfulAppWarning(int userId) {
-        PackageUserState userState = readUserState(userId);
-        return userState.getHarmfulAppWarning();
-    }
-
     /**
      * @see #mPath
      */
@@ -1175,9 +1099,8 @@
     }
 
     /**
-     * @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
-     *
      * @param userId the specific user to change the label/icon for
+     * @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
@@ -1188,9 +1111,8 @@
     }
 
     /**
-     * @see PackageUserStateImpl#resetOverrideComponentLabelIcon()
-     *
      * @param userId the specific user to reset
+     * @see PackageUserStateImpl#resetOverrideComponentLabelIcon()
      */
     public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
         modifyUserState(userId).resetOverrideComponentLabelIcon();
@@ -1198,19 +1120,9 @@
     }
 
     /**
-     * @param userId    the specified user to modify the theme for
-     * @param themeName the theme name to persist
-     * @see android.window.SplashScreen#setSplashScreenTheme(int)
-     */
-    public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
-        modifyUserState(userId).setSplashScreenTheme(themeName);
-        onChanged();
-    }
-
-    /**
      * @param userId the specified user to get the theme setting from
-     * @return the theme name previously persisted for the user or null
-     * if no splashscreen theme is persisted.
+     * @return the theme name previously persisted for the user or null if no splashscreen theme is
+     * persisted.
      * @see android.window.SplashScreen#setSplashScreenTheme(int)
      */
     @Nullable
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 802f701..bb82e6a 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -102,9 +102,7 @@
             if (DEBUG_PREFERRED) {
                 Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
             }
-            synchronized (mPm.mLock) {
-                mPm.scheduleWritePackageRestrictionsLocked(userId);
-            }
+            mPm.scheduleWritePackageRestrictions(userId);
         }
         if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
             Slog.v(TAG, "No preferred activity to return");
@@ -121,9 +119,7 @@
         if (changedUsers.size() > 0) {
             updateDefaultHomeNotLocked(changedUsers);
             mPm.postPreferredActivityChangedBroadcast(userId);
-            synchronized (mPm.mLock) {
-                mPm.scheduleWritePackageRestrictionsLocked(userId);
-            }
+            mPm.scheduleWritePackageRestrictions(userId);
         }
     }
 
@@ -214,7 +210,7 @@
                 Settings.removeFilters(pir, filter, existing);
             }
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
-            mPm.scheduleWritePackageRestrictionsLocked(userId);
+            mPm.scheduleWritePackageRestrictions(userId);
         }
         if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
             mPm.postPreferredActivityChangedBroadcast(userId);
@@ -399,7 +395,7 @@
         synchronized (mPm.mLock) {
             mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
                     new PersistentPreferredActivity(filter, activity, true));
-            mPm.scheduleWritePackageRestrictionsLocked(userId);
+            mPm.scheduleWritePackageRestrictions(userId);
         }
         if (isHomeFilter(filter)) {
             updateDefaultHomeNotLocked(userId);
@@ -420,9 +416,7 @@
         if (changed) {
             updateDefaultHomeNotLocked(userId);
             mPm.postPreferredActivityChangedBroadcast(userId);
-            synchronized (mPm.mLock) {
-                mPm.scheduleWritePackageRestrictionsLocked(userId);
-            }
+            mPm.scheduleWritePackageRestrictions(userId);
         }
     }
 
@@ -603,9 +597,7 @@
             }
             updateDefaultHomeNotLocked(userId);
             resetNetworkPolicies(userId);
-            synchronized (mPm.mLock) {
-                mPm.scheduleWritePackageRestrictionsLocked(userId);
-            }
+            mPm.scheduleWritePackageRestrictions(userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 19f180f..c0e191f 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -29,6 +29,7 @@
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.SharedUserApi;
 
 import libcore.io.IoUtils;
 
@@ -346,7 +347,7 @@
     }
 
     private static int getTargetSdkVersionForSeInfo(AndroidPackage pkg,
-            SharedUserSetting sharedUserSetting, PlatformCompat compatibility) {
+            SharedUserApi sharedUser, PlatformCompat compatibility) {
         // Apps which share a sharedUserId must be placed in the same selinux domain. If this
         // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
         // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
@@ -355,8 +356,8 @@
         // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
         // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
         // ensures that all packages continue to run in the same selinux domain.
-        if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
-            return sharedUserSetting.seInfoTargetSdkVersion;
+        if ((sharedUser != null) && (sharedUser.getPackages().size() != 0)) {
+            return sharedUser.getSeInfoTargetSdkVersion();
         }
         final ApplicationInfo appInfo = AndroidPackageUtils.generateAppInfoWithoutState(pkg);
         if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
@@ -376,18 +377,18 @@
      * the ApplicationInfo instance of the package.
      *
      * @param pkg               object representing the package to be labeled.
-     * @param sharedUserSetting if the app shares a sharedUserId, then this has the shared setting.
+     * @param sharedUser if the app shares a sharedUserId, then this has the shared setting.
      * @param compatibility     the PlatformCompat service to ask about state of compat changes.
      * @return String representing the resulting seinfo.
      */
-    public static String getSeInfo(AndroidPackage pkg, SharedUserSetting sharedUserSetting,
+    public static String getSeInfo(AndroidPackage pkg, SharedUserApi sharedUser,
             PlatformCompat compatibility) {
-        final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUserSetting,
+        final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUser,
                 compatibility);
         // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
         // They currently can be if the sharedUser apps are signed with the platform key.
-        final boolean isPrivileged = (sharedUserSetting != null)
-                ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+        final boolean isPrivileged = (sharedUser != null)
+                ? sharedUser.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
         return getSeInfo(pkg, isPrivileged, targetSdkVersion);
     }
 
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 4345d51..b952f80 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -88,7 +88,7 @@
     /**
      * Notify listeners that this object has changed.
      */
-    protected void onChanged() {
+    public void onChanged() {
         PackageStateMutator.onPackageStateChanged();
         dispatchChange(this);
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f21bc93..13a3c5b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1438,19 +1438,19 @@
         }
     }
 
-    boolean isPermissionUpgradeNeededLPr(int userId) {
+    boolean isPermissionUpgradeNeeded(int userId) {
         return mRuntimePermissionsPersistence.isPermissionUpgradeNeeded(userId);
     }
 
-    void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
+    void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
         mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId);
     }
 
-    int getDefaultRuntimePermissionsVersionLPr(int userId) {
+    int getDefaultRuntimePermissionsVersion(int userId) {
         return mRuntimePermissionsPersistence.getVersion(userId);
     }
 
-    void setDefaultRuntimePermissionsVersionLPr(int version, int userId) {
+    void setDefaultRuntimePermissionsVersion(int version, int userId) {
         mRuntimePermissionsPersistence.setVersion(version, userId);
     }
 
@@ -4288,49 +4288,6 @@
         return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
     }
 
-    boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName,
-            boolean stopped, int userId) {
-        final PackageSetting pkgSetting = mPackages.get(packageName);
-        if (pkgSetting == null) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
-        if (DEBUG_STOPPED) {
-            if (stopped) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Slog.i(TAG, "Stopping package " + packageName, e);
-            }
-        }
-        if (pkgSetting.getStopped(userId) != stopped) {
-            pkgSetting.setStopped(stopped, userId);
-            if (pkgSetting.getNotLaunched(userId)) {
-                if (pkgSetting.getInstallSource().installerPackageName != null) {
-                    pm.notifyFirstLaunch(pkgSetting.getPackageName(),
-                            pkgSetting.getInstallSource().installerPackageName, userId);
-                }
-                pkgSetting.setNotLaunched(false, userId);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    void setHarmfulAppWarningLPw(String packageName, CharSequence warning, int userId) {
-        final PackageSetting pkgSetting = mPackages.get(packageName);
-        if (pkgSetting == null) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
-        pkgSetting.setHarmfulAppWarning(userId, warning == null ? null : warning.toString());
-    }
-
-    String getHarmfulAppWarningLPr(String packageName, int userId) {
-        final PackageSetting pkgSetting = mPackages.get(packageName);
-        if (pkgSetting == null) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
-        return pkgSetting.getHarmfulAppWarning(userId);
-    }
-
     /**
      * Returns all users on the device, including pre-created and dying users.
      *
@@ -4869,6 +4826,9 @@
             date.setTime(pus.getFirstInstallTime());
             pw.println(sdf.format(date));
 
+            pw.print("      uninstallReason=");
+            pw.println(userState.getUninstallReason());
+
             if (userState.isSuspended()) {
                 pw.print(prefix);
                 pw.println("  Suspend params:");
@@ -5150,7 +5110,7 @@
         }
     }
 
-    void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) {
+    void dumpReadMessages(PrintWriter pw, DumpState dumpState) {
         pw.println("Settings parse messages:");
         pw.print(mReadMessages.toString());
     }
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 2227a78..0638d5e 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -42,12 +42,14 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
 import com.android.server.utils.Watchable;
@@ -254,6 +256,7 @@
 
     /**
      * Given the library name, returns a list of shared libraries on all versions.
+     * TODO: Remove, this is used for live mutation outside of the defined commit path
      */
     @GuardedBy("mPm.mLock")
     @Override
@@ -262,6 +265,11 @@
         return mSharedLibraries.get(libName);
     }
 
+    @VisibleForTesting
+    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+        return mSharedLibraries;
+    }
+
     /**
      * Returns the shared library with given library name and version number.
      */
@@ -286,18 +294,19 @@
         return mStaticLibsByDeclaringPackage.get(declaringPackageName);
     }
 
-    @GuardedBy("mPm.mLock")
-    private @Nullable PackageSetting getLibraryPackageLPr(@NonNull SharedLibraryInfo libInfo) {
+    @Nullable
+    private PackageStateInternal getLibraryPackage(@NonNull Computer computer,
+            @NonNull SharedLibraryInfo libInfo) {
         final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
         if (libInfo.isStatic()) {
             // Resolve the package name - we use synthetic package names internally
-            final String internalPackageName = mPm.resolveInternalPackageNameLPr(
+            final String internalPackageName = computer.resolveInternalPackageName(
                     declaringPackage.getPackageName(),
                     declaringPackage.getLongVersionCode());
-            return mPm.mSettings.getPackageLPr(internalPackageName);
+            return computer.getPackageStateInternal(internalPackageName);
         }
         if (libInfo.isSdk()) {
-            return mPm.mSettings.getPackageLPr(declaringPackage.getPackageName());
+            return computer.getPackageStateInternal(declaringPackage.getPackageName());
         }
         return null;
     }
@@ -317,24 +326,26 @@
         final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
 
-        List<VersionedPackage> packagesToDelete = null;
+        final ArrayList<VersionedPackage> packagesToDelete = new ArrayList<>();
         final long now = System.currentTimeMillis();
 
         // Important: We skip shared libs used for some user since
         // in such a case we need to keep the APK on the device. The check for
         // a lib being used for any user is performed by the uninstall call.
-        synchronized (mPm.mLock) {
-            final int libCount = mSharedLibraries.size();
+        mPm.executeWithConsistentComputer(computer -> {
+            final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+                    sharedLibraries = computer.getSharedLibraries();
+            final int libCount = sharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
                 final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
-                        mSharedLibraries.valueAt(i);
+                        sharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
                 final int versionCount = versionedLib.size();
                 for (int j = 0; j < versionCount; j++) {
                     SharedLibraryInfo libInfo = versionedLib.valueAt(j);
-                    final PackageSetting ps = getLibraryPackageLPr(libInfo);
+                    final PackageStateInternal ps = getLibraryPackage(computer, libInfo);
                     if (ps == null) {
                         continue;
                     }
@@ -348,27 +359,22 @@
                         continue;
                     }
 
-                    if (packagesToDelete == null) {
-                        packagesToDelete = new ArrayList<>();
-                    }
                     packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
                             libInfo.getDeclaringPackage().getLongVersionCode()));
                 }
             }
-        }
+        });
 
-        if (packagesToDelete != null) {
-            final int packageCount = packagesToDelete.size();
-            for (int i = 0; i < packageCount; i++) {
-                final VersionedPackage pkgToDelete = packagesToDelete.get(i);
-                // Delete the package synchronously (will fail of the lib used for any user).
-                if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
-                        pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
-                        PackageManager.DELETE_ALL_USERS,
-                        true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
-                    if (volume.getUsableSpace() >= neededSpace) {
-                        return true;
-                    }
+        final int packageCount = packagesToDelete.size();
+        for (int i = 0; i < packageCount; i++) {
+            final VersionedPackage pkgToDelete = packagesToDelete.get(i);
+            // Delete the package synchronously (will fail of the lib used for any user).
+            if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
+                    pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
+                    PackageManager.DELETE_ALL_USERS,
+                    true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
+                if (volume.getUsableSpace() >= neededSpace) {
+                    return true;
                 }
             }
         }
@@ -525,7 +531,7 @@
             @NonNull Map<String, AndroidPackage> availablePackages)
             throws PackageManagerException {
         final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
-                pkgSetting.getPkg(), availablePackages, null /* newLibraries */);
+                pkg, availablePackages, null /* newLibraries */);
         executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
                 sharedLibraryInfos, mPm.mUserManager.getUserIds());
     }
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesRead.java b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
index e6f2311..84d7478f 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesRead.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
@@ -30,7 +30,7 @@
  * An interface implemented by {@link SharedLibrariesImpl} for {@link Computer} to get current
  * shared libraries on the device.
  */
-interface SharedLibrariesRead {
+public interface SharedLibrariesRead {
 
     /**
      * Returns all shared libraries on the device.
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index bc48461..5ef1471 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.SigningDetails;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedProcess;
 import com.android.server.pm.pkg.component.ParsedProcessImpl;
@@ -28,6 +29,8 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.utils.SnapshotCache;
 
 import libcore.util.EmptyArray;
@@ -40,25 +43,26 @@
 /**
  * Settings data for a particular shared user ID we know about.
  */
-public final class SharedUserSetting extends SettingBase {
+public final class SharedUserSetting extends SettingBase implements SharedUserApi {
     final String name;
 
     int userId;
 
-    // flags that are associated with this uid, regardless of any package flags
+    /** @see SharedUserApi#getUidFlags() **/
     int uidFlags;
     int uidPrivateFlags;
 
-    // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
-    // that all apps within the sharedUser run in the same selinux context.
+    /** @see SharedUserApi#getSeInfoTargetSdkVersion() **/
     int seInfoTargetSdkVersion;
 
     final ArraySet<PackageSetting> packages;
+    private ArraySet<PackageStateInternal> mPackagesSnapshot;
 
     // It is possible for a system app to leave shared user ID by an update.
     // We need to keep track of the shadowed PackageSettings so that it is possible to uninstall
     // the update and revert the system app back into the original shared user ID.
     final ArraySet<PackageSetting> mDisabledPackages;
+    private ArraySet<PackageStateInternal> mDisabledPackagesSnapshot;
 
     final PackageSignatures signatures = new PackageSignatures();
     Boolean signaturesChanged;
@@ -98,7 +102,19 @@
         uidFlags = orig.uidFlags;
         uidPrivateFlags = orig.uidPrivateFlags;
         packages = new ArraySet<>(orig.packages);
+        if (!packages.isEmpty()) {
+            mPackagesSnapshot = new ArraySet<>();
+            for (int index = 0; index < packages.size(); index++) {
+                mPackagesSnapshot.add(new PackageSetting(packages.valueAt(index)));
+            }
+        }
         mDisabledPackages = new ArraySet<>(orig.mDisabledPackages);
+        if (!mDisabledPackages.isEmpty()) {
+            mDisabledPackagesSnapshot = new ArraySet<>();
+            for (int index = 0; index < mDisabledPackages.size(); index++) {
+                mDisabledPackagesSnapshot.add(new PackageSetting(mDisabledPackages.valueAt(index)));
+            }
+        }
         // A SigningDetails seems to consist solely of final attributes, so
         // it is safe to copy the reference.
         signatures.mSigningDetails = orig.signatures.mSigningDetails;
@@ -184,10 +200,9 @@
         }
     }
 
-    /**
-     * @return the list of packages that uses this shared UID
-     */
-    public @NonNull List<AndroidPackage> getPackages() {
+    @NonNull
+    @Override
+    public List<AndroidPackage> getPackages() {
         if (packages == null || packages.size() == 0) {
             return Collections.emptyList();
         }
@@ -201,6 +216,7 @@
         return pkgList;
     }
 
+    @Override
     public boolean isPrivileged() {
         return (this.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
@@ -291,4 +307,60 @@
         onChanged();
         return this;
     }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int getUserId() {
+        return userId;
+    }
+
+    @Override
+    public int getUidFlags() {
+        return uidFlags;
+    }
+
+    @Override
+    public int getPrivateUidFlags() {
+        return uidPrivateFlags;
+    }
+
+    @Override
+    public int getSeInfoTargetSdkVersion() {
+        return seInfoTargetSdkVersion;
+    }
+
+    @NonNull
+    @Override
+    public ArraySet<? extends PackageStateInternal> getPackageStates() {
+        if (mPackagesSnapshot != null) {
+            return mPackagesSnapshot;
+        }
+        return packages;
+    }
+
+    @NonNull
+    @Override
+    public ArraySet<? extends PackageStateInternal> getDisabledPackageStates() {
+        if (mDisabledPackagesSnapshot != null) {
+            return mDisabledPackagesSnapshot;
+        }
+        return mDisabledPackages;
+    }
+
+    @NonNull
+    @Override
+    public SigningDetails getSigningDetails() {
+        return signatures.mSigningDetails;
+    }
+
+    @NonNull
+    @Override
+    public ArrayMap<String, ParsedProcess> getProcesses() {
+        return processes;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 72db242..bda2589 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1694,15 +1694,6 @@
             for (int j = 0; j < shareTargetSize; j++) {
                 mShareTargets.get(j).saveToXml(out);
             }
-            synchronized (mLock) {
-                final Map<String, ShortcutInfo> copy = mShortcuts;
-                if (!mTransientShortcuts.isEmpty()) {
-                    copy.putAll(mTransientShortcuts);
-                    mTransientShortcuts.clear();
-                }
-                saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
-                        Collectors.toList()));
-            }
         }
 
         out.endTag(null, TAG_ROOT);
@@ -2418,6 +2409,18 @@
                         })));
     }
 
+    void persistsAllShortcutsAsync() {
+        synchronized (mLock) {
+            final Map<String, ShortcutInfo> copy = mShortcuts;
+            if (!mTransientShortcuts.isEmpty()) {
+                copy.putAll(mTransientShortcuts);
+                mTransientShortcuts.clear();
+            }
+            saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
+                    Collectors.toList()));
+        }
+    }
+
     private void saveShortcutsAsync(
             @NonNull final Collection<ShortcutInfo> shortcuts) {
         Objects.requireNonNull(shortcuts);
@@ -2489,7 +2492,7 @@
                 mIsAppSearchSchemaUpToDate = true;
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Failed to invoke app search pkg="
+            Slog.e(TAG, "Failed to create app search session. pkg="
                     + getPackageName() + " user=" + mShortcutUser.getUserId(), e);
             Objects.requireNonNull(future).completeExceptionally(e);
         } finally {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8393dee..2760578 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -488,7 +488,7 @@
         mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
         mShortcutDumpFiles = new ShortcutDumpFiles(this);
         mIsAppSearchEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, false);
+                SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, true);
 
         if (onlyForPackageManagerApis) {
             return; // Don't do anything further.  For unit tests only.
@@ -736,7 +736,7 @@
             Slog.d(TAG, "unloadUserLocked: user=" + userId);
         }
         // Save all dirty information.
-        saveDirtyInfo();
+        saveDirtyInfo(false);
 
         // Unload
         mUsers.delete(userId);
@@ -1191,6 +1191,10 @@
 
     @VisibleForTesting
     void saveDirtyInfo() {
+        saveDirtyInfo(true);
+    }
+
+    private void saveDirtyInfo(boolean saveShortcutsInAppSearch) {
         if (DEBUG || DEBUG_REBOOT) {
             Slog.d(TAG, "saveDirtyInfo");
         }
@@ -1205,6 +1209,10 @@
                     if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
                         saveBaseStateLocked();
                     } else {
+                        if (saveShortcutsInAppSearch) {
+                            getUserShortcutsLocked(userId).forAllPackages(
+                                    ShortcutPackage::persistsAllShortcutsAsync);
+                        }
                         saveUserLocked(userId);
                     }
                 }
@@ -3719,13 +3727,16 @@
     private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (DEBUG || DEBUG_REBOOT) {
+                Slog.d(TAG, "Shutdown broadcast received.");
+            }
             // Since it cleans up the shortcut directory and rewrite the ShortcutPackageItems
             // in odrder during saveToXml(), it could lead to shortcuts missing when shutdown.
             // We need it so that it can finish up saving before shutdown.
             synchronized (mLock) {
                 if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
                     mHandler.removeCallbacks(mSaveDirtyInfoRunner);
-                    saveDirtyInfo();
+                    saveDirtyInfo(false);
                 }
                 mShutdown.set(true);
             }
@@ -4394,7 +4405,7 @@
 
             // Save to the filesystem.
             scheduleSaveUser(userId);
-            saveDirtyInfo();
+            saveDirtyInfo(false);
 
             // Note, in case of backup, we don't have to wait on bitmap saving, because we don't
             // back up bitmaps anyway.
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index f466ca7..1ea8b24 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -46,14 +46,17 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.SuspendParams;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Predicate;
 
 public final class SuspendPackageHelper {
@@ -107,57 +110,87 @@
             return packageNames;
         }
 
+        final SuspendParams newSuspendParams =
+                SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
-        final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray modifiedUids = new IntArray(packageNames.length);
-        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final boolean[] canSuspend =
-                suspended ? canSuspendPackageForUser(packageNames, userId, callingUid) : null;
+        final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
 
-        for (int i = 0; i < packageNames.length; i++) {
-            final String packageName = packageNames[i];
-            if (callingPackage.equals(packageName)) {
-                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
-                        + (suspended ? "" : "un") + "suspend itself. Ignoring");
-                unactionedPackages.add(packageName);
-                continue;
-            }
-            final PackageSetting pkgSetting;
-            synchronized (mPm.mLock) {
-                pkgSetting = mPm.mSettings.getPackageLPr(packageName);
-                if (pkgSetting == null
-                        || mPm.shouldFilterApplication(pkgSetting, callingUid, userId)) {
-                    Slog.w(TAG, "Could not find package setting for package: " + packageName
-                            + ". Skipping suspending/un-suspending.");
-                    unactionedPackages.add(packageName);
+        ArraySet<String> modifiedPackages = new ArraySet<>();
+
+        mPm.executeWithConsistentComputer(computer -> {
+            final boolean[] canSuspend = suspended
+                    ? canSuspendPackageForUser(computer, packageNames, userId, callingUid) : null;
+            for (int i = 0; i < packageNames.length; i++) {
+                final String packageName = packageNames[i];
+                if (callingPackage.equals(packageName)) {
+                    Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+                            + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                    unmodifiablePackages.add(packageName);
                     continue;
                 }
-            }
-            if (canSuspend != null && !canSuspend[i]) {
-                unactionedPackages.add(packageName);
-                continue;
-            }
-            final boolean packageUnsuspended;
-            final boolean packageModified;
-            synchronized (mPm.mLock) {
-                if (suspended) {
-                    packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
-                            dialogInfo, appExtras, launcherExtras, userId);
-                } else {
-                    packageModified = pkgSetting.removeSuspension(callingPackage, userId);
+                final PackageStateInternal packageState =
+                        computer.getPackageStateInternal(packageName);
+                if (packageState == null
+                        || computer.shouldFilterApplication(packageState, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping suspending/un-suspending.");
+                    unmodifiablePackages.add(packageName);
+                    continue;
                 }
-                packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
+                if (canSuspend != null && !canSuspend[i]) {
+                    unmodifiablePackages.add(packageName);
+                    continue;
+                }
+
+                final ArrayMap<String, SuspendParams> suspendParamsMap =
+                        packageState.getUserStateOrDefault(userId).getSuspendParams();
+                final SuspendParams suspendParams = suspendParamsMap == null
+                        ? null : suspendParamsMap.get(packageName);
+                boolean hasSuspension = suspendParams != null;
+                if (suspended) {
+                    if (hasSuspension) {
+                        // Skip if there's no changes
+                        if (Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
+                                && Objects.equals(suspendParams.getAppExtras(), appExtras)
+                                && Objects.equals(suspendParams.getLauncherExtras(),
+                                launcherExtras)) {
+                            // Carried over API behavior, must notify change even if no change
+                            changedPackagesList.add(packageName);
+                            changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                            continue;
+                        }
+                    }
+                }
+
+                // If size one, the package will be unsuspended from this call
+                boolean packageUnsuspended =
+                        !suspended && CollectionUtils.size(suspendParamsMap) <= 1;
+                if (suspended || packageUnsuspended) {
+                    changedPackagesList.add(packageName);
+                    changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                }
+
+                modifiedPackages.add(packageName);
+                modifiedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
             }
-            if (suspended || packageUnsuspended) {
-                changedPackagesList.add(packageName);
-                changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+        });
+
+        mPm.commitPackageStateMutation(null, mutator -> {
+            final int size = modifiedPackages.size();
+            for (int index = 0; index < size; index++) {
+                final String packageName  = modifiedPackages.valueAt(index);
+                final PackageUserStateWrite userState = mutator.forPackage(packageName)
+                        .userState(userId);
+                if (suspended) {
+                    userState.putSuspendParams(callingPackage, newSuspendParams);
+                } else {
+                    userState.removeSuspension(callingPackage);
+                }
             }
-            if (packageModified) {
-                modifiedPackagesList.add(packageName);
-                modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
-            }
-        }
+        });
 
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(new String[0]);
@@ -166,16 +199,14 @@
                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
                     changedPackages, changedUids.toArray(), userId);
             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
-            synchronized (mPm.mLock) {
-                mPm.scheduleWritePackageRestrictionsLocked(userId);
-            }
+            mPm.scheduleWritePackageRestrictions(userId);
         }
         // Send the suspension changed broadcast to ensure suspension state is not stale.
-        if (!modifiedPackagesList.isEmpty()) {
+        if (!modifiedPackages.isEmpty()) {
             sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
-                    modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+                    modifiedPackages.toArray(new String[0]), modifiedUids.toArray(), userId);
         }
-        return unactionedPackages.toArray(new String[0]);
+        return unmodifiablePackages.toArray(new String[0]);
     }
 
     /**
@@ -194,20 +225,22 @@
             return packageNames;
         }
         final ArraySet<String> unactionablePackages = new ArraySet<>();
-        final boolean[] canSuspend = canSuspendPackageForUser(packageNames, userId, callingUid);
-        for (int i = 0; i < packageNames.length; i++) {
-            if (!canSuspend[i]) {
-                unactionablePackages.add(packageNames[i]);
-                continue;
-            }
-            synchronized (mPm.mLock) {
-                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageNames[i]);
-                if (ps == null || mPm.shouldFilterApplication(ps, callingUid, userId)) {
+        mPm.executeWithConsistentComputer(computer -> {
+            final boolean[] canSuspend = canSuspendPackageForUser(computer, packageNames, userId,
+                    callingUid);
+            for (int i = 0; i < packageNames.length; i++) {
+                if (!canSuspend[i]) {
+                    unactionablePackages.add(packageNames[i]);
+                    continue;
+                }
+                final PackageStateInternal packageState =
+                        computer.getPackageStateFiltered(packageNames[i], callingUid, userId);
+                if (packageState == null) {
                     Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
                     unactionablePackages.add(packageNames[i]);
                 }
             }
-        }
+        });
         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
     }
 
@@ -253,19 +286,53 @@
             @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
         final List<String> unsuspendedPackages = new ArrayList<>();
         final IntArray unsuspendedUids = new IntArray();
-        synchronized (mPm.mLock) {
+        final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
+        mPm.executeWithConsistentComputer(computer -> {
             for (String packageName : packagesToChange) {
-                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
-                if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
-                    ps.removeSuspension(suspendingPackagePredicate, userId);
-                    if (!ps.getUserStateOrDefault(userId).isSuspended()) {
-                        unsuspendedPackages.add(ps.getPackageName());
-                        unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+                final PackageStateInternal packageState =
+                        computer.getPackageStateInternal(packageName);
+                final PackageUserStateInternal packageUserState = packageState == null
+                        ? null : packageState.getUserStateOrDefault(userId);
+                if (packageUserState == null || !packageUserState.isSuspended()) {
+                    continue;
+                }
+
+                ArrayMap<String, SuspendParams> suspendParamsMap = packageUserState.getSuspendParams();
+                int countRemoved = 0;
+                for (int index = 0; index < suspendParamsMap.size(); index++) {
+                    String suspendingPackage = suspendParamsMap.keyAt(index);
+                    if (suspendingPackagePredicate.test(suspendingPackage)) {
+                        ArraySet<String> suspendingPkgsToCommit =
+                                pkgToSuspendingPkgsToCommit.get(packageName);
+                        if (suspendingPkgsToCommit == null) {
+                            suspendingPkgsToCommit = new ArraySet<>();
+                            pkgToSuspendingPkgsToCommit.put(packageName, suspendingPkgsToCommit);
+                        }
+                        suspendingPkgsToCommit.add(suspendingPackage);
+                        countRemoved++;
                     }
                 }
+
+                // Everything would be removed and package unsuspended
+                if (countRemoved == suspendParamsMap.size()) {
+                    unsuspendedPackages.add(packageState.getPackageName());
+                    unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                }
             }
-            mPm.scheduleWritePackageRestrictionsLocked(userId);
-        }
+        });
+
+        mPm.commitPackageStateMutation(null, mutator -> {
+            for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
+                String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
+                ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
+                PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId);
+                for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
+                    userState.removeSuspension(packagesToRemove.valueAt(setIndex));
+                }
+            }
+        });
+
+        mPm.scheduleWritePackageRestrictions(userId);
         if (!unsuspendedPackages.isEmpty()) {
             final String[] packageArray = unsuspendedPackages.toArray(
                     new String[unsuspendedPackages.size()]);
@@ -406,7 +473,8 @@
      * @return An array containing results of the checks
      */
     @NonNull
-    boolean[] canSuspendPackageForUser(@NonNull String[] packageNames, int userId, int callingUid) {
+    boolean[] canSuspendPackageForUser(@NonNull Computer computer, @NonNull String[] packageNames,
+            int userId, int callingUid) {
         final boolean[] canSuspend = new boolean[packageNames.length];
         final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId, callingUid);
         final long token = Binder.clearCallingIdentity();
@@ -459,36 +527,38 @@
                             + "\": required for permissions management");
                     continue;
                 }
-                synchronized (mPm.mLock) {
-                    if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-                        Slog.w(TAG, "Cannot suspend package \"" + packageName
-                                + "\": protected package");
-                        continue;
-                    }
-                    if (!isCallerOwner && mPm.mSettings.getBlockUninstallLPr(userId, packageName)) {
-                        Slog.w(TAG, "Cannot suspend package \"" + packageName
-                                + "\": blocked by admin");
-                        continue;
-                    }
+                if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": protected package");
+                    continue;
+                }
+                if (!isCallerOwner && computer.getBlockUninstall(userId, packageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": blocked by admin");
+                    continue;
+                }
 
-                    AndroidPackage pkg = mPm.mPackages.get(packageName);
-                    if (pkg != null) {
-                        // Cannot suspend SDK libs as they are controlled by SDK manager.
-                        if (pkg.isSdkLibrary()) {
-                            Slog.w(TAG, "Cannot suspend package: " + packageName
-                                    + " providing SDK library: "
-                                    + pkg.getSdkLibName());
-                            continue;
-                        }
-                        // Cannot suspend static shared libs as they are considered
-                        // a part of the using app (emulating static linking). Also
-                        // static libs are installed always on internal storage.
-                        if (pkg.isStaticSharedLibrary()) {
-                            Slog.w(TAG, "Cannot suspend package: " + packageName
-                                    + " providing static shared library: "
-                                    + pkg.getStaticSharedLibName());
-                            continue;
-                        }
+                // Cannot suspend static shared libs as they are considered
+                // a part of the using app (emulating static linking). Also
+                // static libs are installed always on internal storage.
+                PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
+                AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
+                if (pkg != null) {
+                    // Cannot suspend SDK libs as they are controlled by SDK manager.
+                    if (pkg.isSdkLibrary()) {
+                        Slog.w(TAG, "Cannot suspend package: " + packageName
+                                + " providing SDK library: "
+                                + pkg.getSdkLibName());
+                        continue;
+                    }
+                    // Cannot suspend static shared libs as they are considered
+                    // a part of the using app (emulating static linking). Also
+                    // static libs are installed always on internal storage.
+                    if (pkg.isStaticSharedLibrary()) {
+                        Slog.w(TAG, "Cannot suspend package: " + packageName
+                                + " providing static shared library: "
+                                + pkg.getStaticSharedLibName());
+                        continue;
                     }
                 }
                 if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d6e88f4..b07cd106 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2418,6 +2418,51 @@
     }
 
     /**
+     * Returns the remaining number of users of the given type that can be created. (taking into
+     * account the total number of users on the device as well as how many exist of that type)
+     */
+    @Override
+    public int getRemainingCreatableUserCount(String userType) {
+        checkQueryOrCreateUsersPermission("get the remaining number of users that can be added.");
+        final UserTypeDetails type = mUserTypes.get(userType);
+        if (type == null || !type.isEnabled()) {
+            return 0;
+        }
+        synchronized (mUsersLock) {
+            final int userCount = getAliveUsersExcludingGuestsCountLU();
+
+            // Limit total number of users that can be created (except for guest and demo)
+            int result =
+                    UserManager.isUserTypeGuest(userType) || UserManager.isUserTypeDemo(userType)
+                        ? Integer.MAX_VALUE
+                        : (UserManager.getMaxSupportedUsers() - userCount);
+
+            // Managed profiles have their own specific rules.
+            if (type.isManagedProfile()) {
+                if (!mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_MANAGED_USERS)) {
+                    return 0;
+                }
+                // Special case: Allow creating a managed profile anyway if there's only 1 user
+                if (result <= 0 & userCount == 1) {
+                    result = 1;
+                }
+            }
+            if (result <= 0) {
+                return 0;
+            }
+
+            // Limit against max allowed for type
+            result = Math.min(result,
+                    type.getMaxAllowed() == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS
+                        ? Integer.MAX_VALUE
+                        : (type.getMaxAllowed() - getNumberOfUsersOfType(userType)));
+
+            return Math.max(0, result);
+        }
+    }
+
+    /**
      * Gets the number of users of the given user type.
      * Does not include users that are about to die.
      */
@@ -2467,24 +2512,36 @@
     @Override
     public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
             boolean allowedToRemoveOne) {
-        checkQueryUsersPermission("check if more profiles can be added.");
+        return 0 < getRemainingCreatableProfileCount(userType, userId, allowedToRemoveOne);
+    }
+
+    /**
+     * Returns the remaining number of profiles of the given type that can be added to the given
+     * user. (taking into account the total number of users on the device as well as how many
+     * profiles exist of that type both in general and for the given user)
+     */
+    @Override
+    public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId,
+            boolean allowedToRemoveOne) {
+        checkQueryOrCreateUsersPermission(
+                "get the remaining number of profiles that can be added to the given user.");
         final UserTypeDetails type = mUserTypes.get(userType);
         if (type == null || !type.isEnabled()) {
-            return false;
+            return 0;
         }
         // Managed profiles have their own specific rules.
         final boolean isManagedProfile = type.isManagedProfile();
         if (isManagedProfile) {
             if (!mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_MANAGED_USERS)) {
-                return false;
+                return 0;
             }
         }
         synchronized (mUsersLock) {
             // Check if the parent exists and its type is even allowed to have a profile.
             UserInfo userInfo = getUserInfoLU(userId);
             if (userInfo == null || !userInfo.canHaveProfile()) {
-                return false;
+                return 0;
             }
 
             final int userTypeCount = getProfileIds(userId, userType, false).length;
@@ -2493,23 +2550,30 @@
                     - profilesRemovedCount;
 
             // Limit total number of users that can be created
-            if (usersCountAfterRemoving >= UserManager.getMaxSupportedUsers()) {
-                // Special case: Allow creating a managed profile anyway if there's only 1 user
-                // Otherwise, disallow.
-                if (!(isManagedProfile && usersCountAfterRemoving == 1)) {
-                    return false;
-                }
+            int result = UserManager.getMaxSupportedUsers() - usersCountAfterRemoving;
+
+            // Special case: Allow creating a managed profile anyway if there's only 1 user
+            if (result <= 0 && isManagedProfile && usersCountAfterRemoving == 1) {
+                result = 1;
             }
 
             // Limit the number of profiles of this type that can be created.
             final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
             if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
-                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
-                    return false;
-                }
+                result = Math.min(result, maxUsersOfType - (userTypeCount - profilesRemovedCount));
             }
+            if (result <= 0) {
+                return 0;
+            }
+
+            // Limit against max allowed for type (beyond max allowed per parent)
+            if (type.getMaxAllowed() != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+                result = Math.min(result, type.getMaxAllowed()
+                        - (getNumberOfUsersOfType(userType) - profilesRemovedCount));
+            }
+
+            return Math.max(0, result);
         }
-        return true;
     }
 
     @GuardedBy("mUsersLock")
@@ -3791,6 +3855,7 @@
                                     + ". Maximum number of that type already exists.",
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
+                // Keep logic in sync with getRemainingCreatableUserCount()
                 if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
                     // If the user limit has been reached, we cannot add a user (except guest/demo).
                     // Note that managed profiles can bypass it in certain circumstances (taken
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index a1a6f5a..9fb1f8f 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -30,11 +30,13 @@
 import android.util.DebugUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
+import android.util.SparseArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -211,31 +213,66 @@
         Slog.i(TAG, "Reviewing whitelisted packages due to "
                 + (isFirstBoot ? "[firstBoot]" : "") + (isConsideredUpgrade ? "[upgrade]" : ""));
         final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+
+        // User ID -> package name -> installed
+        SparseArrayMap<String, Boolean> changesToCommit = new SparseArrayMap<>();
+
         // Install/uninstall system packages per user.
         for (int userId : mUm.getUserIds()) {
             final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
-            pmInt.forEachPackageSetting(pkgSetting -> {
-                AndroidPackage pkg = pkgSetting.getPkg();
-                if (pkg == null || !pkg.isSystem()) {
-                    return;
+
+            // If null, run for all packages
+            if (userWhitelist == null) {
+                pmInt.forEachPackageState(packageState -> {
+                    if (packageState.getPkg() == null) {
+                        return;
+                    }
+                    final boolean install = !packageState.getTransientState()
+                            .isHiddenUntilInstalled();
+                    if (packageState.getUserStateOrDefault(userId).isInstalled() != install
+                            && shouldChangeInstallationState(packageState, install, userId,
+                            isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
+                        changesToCommit.add(userId, packageState.getPackageName(), install);
+                    }
+                });
+            } else {
+                for (String packageName : userWhitelist) {
+                    PackageStateInternal packageState = pmInt.getPackageStateInternal(packageName);
+                    if (packageState.getPkg() == null) {
+                        continue;
+                    }
+
+                    final boolean install = !packageState.getTransientState()
+                            .isHiddenUntilInstalled();
+                    if (packageState.getUserStateOrDefault(userId).isInstalled() != install
+                            && shouldChangeInstallationState(packageState, install, userId,
+                            isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
+                        changesToCommit.add(userId, packageState.getPackageName(), install);
+                    }
                 }
-                final boolean install =
-                        (userWhitelist == null || userWhitelist.contains(pkg.getPackageName()))
-                                && !pkgSetting.getPkgState().isHiddenUntilInstalled();
-                if (pkgSetting.getInstalled(userId) == install
-                        || !shouldChangeInstallationState(pkgSetting, install, userId, isFirstBoot,
-                                isConsideredUpgrade, preExistingPackages)) {
-                    return;
-                }
-                pkgSetting.setInstalled(install, userId);
-                pkgSetting.setUninstallReason(
-                        install ? PackageManager.UNINSTALL_REASON_UNKNOWN :
-                                PackageManager.UNINSTALL_REASON_USER_TYPE,
-                        userId);
-                Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
-                        + pkg.getPackageName() + " for user " + userId);
-            });
+            }
         }
+
+        pmInt.commitPackageStateMutation(null, packageStateMutator -> {
+            for (int userIndex = 0; userIndex < changesToCommit.numMaps(); userIndex++) {
+                int userId = changesToCommit.keyAt(userIndex);
+                int packagesSize = changesToCommit.numElementsForKey(userId);
+                for (int packageIndex = 0; packageIndex < packagesSize; ++packageIndex) {
+                    String packageName = changesToCommit.keyAt(userIndex, packageIndex);
+                    boolean installed = changesToCommit.valueAt(userIndex, packageIndex);
+                    packageStateMutator.forPackage(packageName)
+                            .userState(userId)
+                            .setInstalled(installed)
+                            .setUninstallReason(installed
+                                    ? PackageManager.UNINSTALL_REASON_UNKNOWN
+                                    : PackageManager.UNINSTALL_REASON_USER_TYPE);
+
+                    Slog.i(TAG + "CommitDebug", (installed ? "Installed " : "Uninstalled ")
+                            + packageName + " for user " + userId);
+                }
+            }
+        });
+
         return true;
     }
 
@@ -250,7 +287,7 @@
      * @param preOtaPkgs list of packages on the device prior to the upgrade.
      *                   Cannot be null if isUpgrade is true.
      */
-    private static boolean shouldChangeInstallationState(PackageSetting pkgSetting,
+    private static boolean shouldChangeInstallationState(PackageStateInternal packageState,
                                                          boolean install,
                                                          @UserIdInt int userId,
                                                          boolean isFirstBoot,
@@ -258,11 +295,12 @@
                                                          @Nullable ArraySet<String> preOtaPkgs) {
         if (install) {
             // Only proceed with install if we are the only reason why it had been uninstalled.
-            return pkgSetting.getUninstallReason(userId)
+            return packageState.getUserStateOrDefault(userId).getUninstallReason()
                     == PackageManager.UNINSTALL_REASON_USER_TYPE;
         } else {
             // Only proceed with uninstall if the package is new to the device.
-            return isFirstBoot || (isUpgrade && !preOtaPkgs.contains(pkgSetting.getPackageName()));
+            return isFirstBoot
+                    || (isUpgrade && !preOtaPkgs.contains(packageState.getPackageName()));
         }
     }
 
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 2d0a3ef..63469cb 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -34,19 +34,6 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingUtils;
-import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.PackageUserStateUtils;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -60,6 +47,19 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUnserialized;
 import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedPermission;
+import com.android.server.pm.pkg.component.ParsedPermissionGroup;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingUtils;
 
 import libcore.util.EmptyArray;
 
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 564585b..97d526d 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -18,10 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import com.android.server.pm.pkg.component.ParsedComponent;
 import android.util.Pair;
 
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedComponent;
 
 /**
  * For exposing internal fields to the rest of the server, enforcing that any overridden state from
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 8e41c9b..db9c1b5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -4486,7 +4486,7 @@
     @Override
     public void readLegacyPermissionStateTEMP() {
         final int[] userIds = getAllUserIds();
-        mPackageManagerInt.forEachPackageSetting(ps -> {
+        mPackageManagerInt.forEachPackageState(ps -> {
             final int appId = ps.getAppId();
             final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 56f62ab..fb2fe1f 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -24,7 +24,6 @@
 
 import com.android.server.pm.InstallSource;
 import com.android.server.pm.PackageKeySetData;
-import com.android.server.pm.SharedUserSetting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.LegacyPermissionState;
 
@@ -52,7 +51,7 @@
     InstallSource getInstallSource();
 
     @Nullable
-    SharedUserSetting getSharedUser();
+    SharedUserApi getSharedUser();
 
     // TODO: Remove this in favor of boolean APIs
     int getFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index ef21543..7bd720a 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -40,6 +40,7 @@
  * where they would be lost implicitly by re-generating the package object.
  */
 @DataClass(genSetters = true, genConstructor = false, genBuilder = false)
+@DataClass.Suppress({"setLastPackageUsageTimeInMills", "setPackageSetting"})
 public class PackageStateUnserialized {
 
     private boolean hiddenUntilInstalled;
@@ -58,6 +59,14 @@
     @Nullable
     private String overrideSeInfo;
 
+    // TODO: Remove in favor of finer grained change notification
+    @NonNull
+    private final PackageSetting mPackageSetting;
+
+    public PackageStateUnserialized(@NonNull PackageSetting packageSetting) {
+        mPackageSetting = packageSetting;
+    }
+
     private long[] lazyInitLastPackageUsageTimeInMills() {
         return new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
     }
@@ -70,6 +79,7 @@
             return this;
         }
         getLastPackageUsageTimeInMills()[reason] = time;
+        mPackageSetting.onChanged();
         return this;
     }
 
@@ -108,6 +118,7 @@
         this.updatedSystemApp = other.updatedSystemApp;
         this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills;
         this.overrideSeInfo = other.overrideSeInfo;
+        mPackageSetting.onChanged();
     }
 
     public @NonNull List<SharedLibraryInfo> getNonNativeUsesLibraryInfos() {
@@ -115,8 +126,45 @@
                 .filter((l) -> !l.isNative()).collect(Collectors.toList());
     }
 
+    public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
+        hiddenUntilInstalled = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
 
-    // Code below generated by codegen v1.0.14.
+    public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
+        usesLibraryInfos = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+    public PackageStateUnserialized setUsesLibraryFiles(@NonNull List<String> value) {
+        usesLibraryFiles = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+    public PackageStateUnserialized setUpdatedSystemApp(boolean value) {
+        updatedSystemApp = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+    public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
+        lastPackageUsageTimeInMills = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+    public PackageStateUnserialized setOverrideSeInfo(@Nullable String value) {
+        overrideSeInfo = value;
+        mPackageSetting.onChanged();
+        return this;
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -169,52 +217,15 @@
     }
 
     @DataClass.Generated.Member
-    public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
-        hiddenUntilInstalled = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
-        usesLibraryInfos = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, usesLibraryInfos);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public PackageStateUnserialized setUsesLibraryFiles(@NonNull List<String> value) {
-        usesLibraryFiles = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, usesLibraryFiles);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public PackageStateUnserialized setUpdatedSystemApp(boolean value) {
-        updatedSystemApp = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
-        lastPackageUsageTimeInMills = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, lastPackageUsageTimeInMills);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public PackageStateUnserialized setOverrideSeInfo(@Nullable String value) {
-        overrideSeInfo = value;
-        return this;
+    public @NonNull PackageSetting getPackageSetting() {
+        return mPackageSetting;
     }
 
     @DataClass.Generated(
-            time = 1580422870209L,
-            codegenVersion = "1.0.14",
+            time = 1642554781099L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java",
-            inputSignatures = "private  boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate  boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\n @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate  long[] lazyInitLastPackageUsageTimeInMills()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic  long getLatestPackageUseTimeInMills()\npublic  long getLatestForegroundPackageUseTimeInMills()\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
+            inputSignatures = "private  boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate  boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate  long[] lazyInitLastPackageUsageTimeInMills()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic  long getLatestPackageUseTimeInMills()\npublic  long getLatestForegroundPackageUseTimeInMills()\npublic  void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic  com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic  com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
new file mode 100644
index 0000000..43eac53
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -0,0 +1,68 @@
+/*
+ * 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.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.SigningDetails;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.component.ParsedProcess;
+
+import java.util.List;
+
+public interface SharedUserApi {
+
+    @NonNull
+    String getName();
+
+    @UserIdInt
+    int getUserId();
+
+    // flags that are associated with this uid, regardless of any package flags
+    int getUidFlags();
+    int getPrivateUidFlags();
+
+    // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
+    // that all apps within the sharedUser run in the same selinux context.
+    int getSeInfoTargetSdkVersion();
+
+    /**
+     * @return the list of packages that uses this shared UID
+     */
+    @NonNull
+    List<AndroidPackage> getPackages();
+
+    @NonNull
+    ArraySet<? extends PackageStateInternal> getPackageStates();
+
+    // It is possible for a system app to leave shared user ID by an update.
+    // We need to keep track of the shadowed PackageSettings so that it is possible to uninstall
+    // the update and revert the system app back into the original shared user ID.
+    @NonNull
+    ArraySet<? extends PackageStateInternal> getDisabledPackageStates();
+
+    @NonNull
+    SigningDetails getSigningDetails();
+
+    @NonNull
+    ArrayMap<String, ParsedProcess> getProcesses();
+
+    boolean isPrivileged();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 35d4d9e..951ddfa 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.overlay.OverlayPaths;
@@ -177,6 +178,13 @@
         }
 
         @Override
+        public void onChanged() {
+            if (mState != null) {
+                mState.onChanged();
+            }
+        }
+
+        @Override
         public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
             if (mState != null) {
                 mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
@@ -217,6 +225,51 @@
             return this;
         }
 
+        @NonNull
+        @Override
+        public PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category) {
+            if (mState != null) {
+                mState.setCategoryOverride(category);
+            }
+            return this;
+        }
+
+        @NonNull
+        @Override
+        public PackageStateWrite setUpdateAvailable(boolean updateAvailable) {
+            if (mState != null) {
+                mState.setUpdateAvailable(updateAvailable);
+            }
+            return this;
+        }
+
+        @NonNull
+        @Override
+        public PackageStateWrite setLoadingProgress(float progress) {
+            if (mState != null) {
+                mState.setLoadingProgress(progress);
+            }
+            return this;
+        }
+
+        @NonNull
+        @Override
+        public PackageStateWrite setOverrideSeInfo(@Nullable String newSeInfo) {
+            if (mState != null) {
+                mState.getTransientState().setOverrideSeInfo(newSeInfo);
+            }
+            return this;
+        }
+
+        @NonNull
+        @Override
+        public PackageStateWrite setInstaller(@NonNull String installerPackageName) {
+            if (mState != null) {
+                mState.setInstallerPackageName(installerPackageName);
+            }
+            return this;
+        }
+
         private static class UserStateWriteWrapper implements PackageUserStateWrite {
 
             @Nullable
@@ -328,6 +381,25 @@
                 }
                 return this;
             }
+
+            @NonNull
+            @Override
+            public PackageUserStateWrite setSplashScreenTheme(@Nullable String theme) {
+                if (mUserState != null) {
+                    mUserState.setSplashScreenTheme(theme);
+                }
+                return this;
+            }
+
+            @NonNull
+            @Override
+            public PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
+                    @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
+                if (mUserState != null) {
+                    mUserState.overrideLabelAndIcon(componentName, nonLocalizedLabel, icon);
+                }
+                return null;
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
index 585bece..1ac0b05 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
@@ -17,12 +17,16 @@
 package com.android.server.pm.pkg.mutate;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.util.ArraySet;
 
 public interface PackageStateWrite {
 
+    void onChanged();
+
     @NonNull
     PackageUserStateWrite userState(@UserIdInt int userId);
 
@@ -38,4 +42,19 @@
 
     @NonNull
     PackageStateWrite setMimeGroup(@NonNull String mimeGroup, @NonNull ArraySet<String> mimeTypes);
+
+    @NonNull
+    PackageStateWrite setCategoryOverride(@ApplicationInfo.Category int category);
+
+    @NonNull
+    PackageStateWrite setUpdateAvailable(boolean updateAvailable);
+
+    @NonNull
+    PackageStateWrite setLoadingProgress(float progress);
+
+    @NonNull
+    PackageStateWrite setOverrideSeInfo(@Nullable String newSeInfo);
+
+    @NonNull
+    PackageStateWrite setInstaller(@NonNull String installerPackageName);
 }
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index e23a1b6..11d6d97 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.overlay.OverlayPaths;
 
@@ -60,4 +61,11 @@
 
     @NonNull
     PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning);
+
+    @NonNull
+    PackageUserStateWrite setSplashScreenTheme(@Nullable String theme);
+
+    @NonNull
+    PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
+            @Nullable String nonLocalizedLabel, @Nullable Integer icon);
 }
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
new file mode 100644
index 0000000..cea84b5
--- /dev/null
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Controls Low Power Standby state.
+ *
+ * Instantiated by {@link PowerManagerService} only if Low Power Standby is supported.
+ *
+ * <p>Low Power Standby is active when all of the following conditions are met:
+ * <ul>
+ *   <li>Low Power Standby is enabled
+ *   <li>The device is not interactive, and has been non-interactive for a given timeout
+ *   <li>The device is not in a doze maintenance window
+ * </ul>
+ *
+ * <p>When Low Power Standby is active, the following restrictions are applied to applications
+ * with procstate less important than {@link android.app.ActivityManager#PROCESS_STATE_BOUND_TOP}:
+ * <ul>
+ *   <li>Network access is blocked
+ *   <li>Wakelocks are disabled
+ * </ul>
+ *
+ * @hide
+ */
+public final class LowPowerStandbyController {
+    private static final String TAG = "LowPowerStandbyController";
+    private static final boolean DEBUG = false;
+    private static final boolean DEFAULT_ACTIVE_DURING_MAINTENANCE = false;
+
+    private static final int MSG_STANDBY_TIMEOUT = 0;
+    private static final int MSG_NOTIFY_ACTIVE_CHANGED = 1;
+    private static final int MSG_NOTIFY_ALLOWLIST_CHANGED = 2;
+
+    private final Handler mHandler;
+    private final SettingsObserver mSettingsObserver;
+    private final Object mLock = new Object();
+
+    private final Context mContext;
+    private final Clock mClock;
+    private final AlarmManager.OnAlarmListener mOnStandbyTimeoutExpired =
+            this::onStandbyTimeoutExpired;
+    private final LowPowerStandbyControllerInternal mLocalService = new LocalService();
+    private final SparseBooleanArray mAllowlistUids = new SparseBooleanArray();
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_SCREEN_OFF:
+                    onNonInteractive();
+                    break;
+                case Intent.ACTION_SCREEN_ON:
+                    onInteractive();
+                    break;
+                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                    onDeviceIdleModeChanged();
+                    break;
+            }
+        }
+    };
+
+    @GuardedBy("mLock")
+    private AlarmManager mAlarmManager;
+    @GuardedBy("mLock")
+    private PowerManager mPowerManager;
+    @GuardedBy("mLock")
+    private boolean mSupportedConfig;
+    @GuardedBy("mLock")
+    private boolean mEnabledByDefaultConfig;
+    @GuardedBy("mLock")
+    private int mStandbyTimeoutConfig;
+
+    /** Whether Low Power Standby is enabled in Settings */
+    @GuardedBy("mLock")
+    private boolean mIsEnabled;
+
+    /**
+     * Whether Low Power Standby is currently active (enforcing restrictions).
+     */
+    @GuardedBy("mLock")
+    private boolean mIsActive;
+
+    /** Whether the device is currently interactive */
+    @GuardedBy("mLock")
+    private boolean mIsInteractive;
+
+    /** The time the device was last interactive, in {@link SystemClock#elapsedRealtime()}. */
+    @GuardedBy("mLock")
+    private long mLastInteractiveTimeElapsed;
+
+    /**
+     * Whether we are in device idle mode.
+     * During maintenance windows Low Power Standby is deactivated to allow
+     * apps to run maintenance tasks.
+     */
+    @GuardedBy("mLock")
+    private boolean mIsDeviceIdle;
+
+    /**
+     * Whether the device has entered idle mode since becoming non-interactive.
+     * In the initial non-idle period after turning the screen off, Low Power Standby is already
+     * allowed to become active. Later non-idle periods are treated as maintenance windows, during
+     * which Low Power Standby is deactivated to allow apps to run maintenance tasks.
+     */
+    @GuardedBy("mLock")
+    private boolean mIdleSinceNonInteractive;
+
+    /** Whether Low Power Standby restrictions should be active during doze maintenance mode. */
+    @GuardedBy("mLock")
+    private boolean mActiveDuringMaintenance;
+
+    /** Force Low Power Standby to be active. */
+    @GuardedBy("mLock")
+    private boolean mForceActive;
+
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /** Returns milliseconds since boot, including time spent in sleep. */
+        long elapsedRealtime();
+    }
+
+    public LowPowerStandbyController(Context context, Looper looper, Clock clock) {
+        mContext = context;
+        mHandler = new LowPowerStandbyHandler(looper);
+        mClock = clock;
+        mSettingsObserver = new SettingsObserver(mHandler);
+    }
+
+    void systemReady() {
+        final Resources resources = mContext.getResources();
+        synchronized (mLock) {
+            mSupportedConfig = resources.getBoolean(
+                    com.android.internal.R.bool.config_lowPowerStandbySupported);
+
+            if (!mSupportedConfig) {
+                return;
+            }
+
+            mAlarmManager = mContext.getSystemService(AlarmManager.class);
+            mPowerManager = mContext.getSystemService(PowerManager.class);
+
+            mStandbyTimeoutConfig = resources.getInteger(
+                    R.integer.config_lowPowerStandbyNonInteractiveTimeout);
+            mEnabledByDefaultConfig = resources.getBoolean(
+                    R.bool.config_lowPowerStandbyEnabledByDefault);
+
+            mIsInteractive = mPowerManager.isInteractive();
+
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.LOW_POWER_STANDBY_ENABLED),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
+            updateSettingsLocked();
+
+            if (mIsEnabled) {
+                registerBroadcastReceiver();
+            }
+        }
+
+        LocalServices.addService(LowPowerStandbyControllerInternal.class, mLocalService);
+    }
+
+    @GuardedBy("mLock")
+    private void updateSettingsLocked() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        mIsEnabled = mSupportedConfig && Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_STANDBY_ENABLED,
+                mEnabledByDefaultConfig ? 1 : 0) != 0;
+        mActiveDuringMaintenance = Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+                DEFAULT_ACTIVE_DURING_MAINTENANCE ? 1 : 0) != 0;
+
+        updateActiveLocked();
+    }
+
+    @GuardedBy("mLock")
+    private void updateActiveLocked() {
+        final long now = mClock.elapsedRealtime();
+        final boolean standbyTimeoutExpired =
+                (now - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
+        final boolean maintenanceMode = mIdleSinceNonInteractive && !mIsDeviceIdle;
+        final boolean newActive =
+                mForceActive || (mIsEnabled && !mIsInteractive && standbyTimeoutExpired
+                        && (!maintenanceMode || mActiveDuringMaintenance));
+        if (DEBUG) {
+            Slog.d(TAG, "updateActiveLocked: mIsEnabled=" + mIsEnabled + ", mIsInteractive="
+                    + mIsInteractive + ", standbyTimeoutExpired=" + standbyTimeoutExpired
+                    + ", mIdleSinceNonInteractive=" + mIdleSinceNonInteractive + ", mIsDeviceIdle="
+                    + mIsDeviceIdle + ", mActiveDuringMaintenance=" + mActiveDuringMaintenance
+                    + ", mForceActive=" + mForceActive + ", mIsActive=" + mIsActive + ", newActive="
+                    + newActive);
+        }
+        if (mIsActive != newActive) {
+            mIsActive = newActive;
+            if (DEBUG) {
+                Slog.d(TAG, "mIsActive changed, mIsActive=" + mIsActive);
+            }
+            enqueueNotifyActiveChangedLocked();
+        }
+    }
+
+    private void onNonInteractive() {
+        if (DEBUG) {
+            Slog.d(TAG, "onNonInteractive");
+        }
+        final long now = mClock.elapsedRealtime();
+        synchronized (mLock) {
+            mIsInteractive = false;
+            mIsDeviceIdle = false;
+            mLastInteractiveTimeElapsed = now;
+
+            if (mStandbyTimeoutConfig > 0) {
+                scheduleStandbyTimeoutAlarmLocked();
+            }
+
+            updateActiveLocked();
+        }
+    }
+
+    private void onInteractive() {
+        if (DEBUG) {
+            Slog.d(TAG, "onInteractive");
+        }
+
+        synchronized (mLock) {
+            cancelStandbyTimeoutAlarmLocked();
+            mIsInteractive = true;
+            mIsDeviceIdle = false;
+            mIdleSinceNonInteractive = false;
+            updateActiveLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void scheduleStandbyTimeoutAlarmLocked() {
+        final long nextAlarmTime = SystemClock.elapsedRealtime() + mStandbyTimeoutConfig;
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                nextAlarmTime, "LowPowerStandbyController.StandbyTimeout",
+                mOnStandbyTimeoutExpired, mHandler);
+    }
+
+    @GuardedBy("mLock")
+    private void cancelStandbyTimeoutAlarmLocked() {
+        mAlarmManager.cancel(mOnStandbyTimeoutExpired);
+    }
+
+    private void onDeviceIdleModeChanged() {
+        synchronized (mLock) {
+            mIsDeviceIdle = mPowerManager.isDeviceIdleMode();
+            if (DEBUG) {
+                Slog.d(TAG, "onDeviceIdleModeChanged, mIsDeviceIdle=" + mIsDeviceIdle);
+            }
+
+            mIdleSinceNonInteractive = mIdleSinceNonInteractive || mIsDeviceIdle;
+            updateActiveLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onEnabledLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "onEnabledLocked");
+        }
+
+        if (mPowerManager.isInteractive()) {
+            onInteractive();
+        } else {
+            onNonInteractive();
+        }
+
+        registerBroadcastReceiver();
+    }
+
+    @GuardedBy("mLock")
+    private void onDisabledLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "onDisabledLocked");
+        }
+
+        cancelStandbyTimeoutAlarmLocked();
+        unregisterBroadcastReceiver();
+        updateActiveLocked();
+    }
+
+    @VisibleForTesting
+    void onSettingsChanged() {
+        if (DEBUG) {
+            Slog.d(TAG, "onSettingsChanged");
+        }
+        synchronized (mLock) {
+            final boolean oldEnabled = mIsEnabled;
+            updateSettingsLocked();
+
+            if (mIsEnabled != oldEnabled) {
+                if (mIsEnabled) {
+                    onEnabledLocked();
+                } else {
+                    onDisabledLocked();
+                }
+
+                notifyEnabledChangedLocked();
+            }
+        }
+    }
+
+    private void registerBroadcastReceiver() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
+        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+    }
+
+    private void unregisterBroadcastReceiver() {
+        mContext.unregisterReceiver(mBroadcastReceiver);
+    }
+
+    @GuardedBy("mLock")
+    private void notifyEnabledChangedLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyEnabledChangedLocked, mIsEnabled=" + mIsEnabled);
+        }
+
+        final Intent intent = new Intent(PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    private void onStandbyTimeoutExpired() {
+        if (DEBUG) {
+            Slog.d(TAG, "onStandbyTimeoutExpired");
+        }
+        synchronized (mLock) {
+            updateActiveLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void enqueueNotifyActiveChangedLocked() {
+        final long now = mClock.elapsedRealtime();
+        final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ACTIVE_CHANGED, mIsActive);
+        mHandler.sendMessageAtTime(msg, now);
+    }
+
+    /** Notify other system components about the updated Low Power Standby active state */
+    private void notifyActiveChanged(boolean active) {
+        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+        pmi.setLowPowerStandbyActive(active);
+    }
+
+    @VisibleForTesting
+    boolean isActive() {
+        synchronized (mLock) {
+            return mIsActive;
+        }
+    }
+
+    boolean isSupported() {
+        synchronized (mLock) {
+            return mSupportedConfig;
+        }
+    }
+
+    boolean isEnabled() {
+        synchronized (mLock) {
+            return mSupportedConfig && mIsEnabled;
+        }
+    }
+
+    void setEnabled(boolean enabled) {
+        synchronized (mLock) {
+            if (!mSupportedConfig) {
+                Slog.w(TAG, "Low Power Standby cannot be enabled "
+                        + "because it is not supported on this device");
+                return;
+            }
+
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.LOW_POWER_STANDBY_ENABLED, enabled ? 1 : 0);
+            onSettingsChanged();
+        }
+    }
+
+    void setActiveDuringMaintenance(boolean activeDuringMaintenance) {
+        synchronized (mLock) {
+            if (!mSupportedConfig) {
+                Slog.w(TAG, "Low Power Standby settings cannot be changed "
+                        + "because it is not supported on this device");
+                return;
+            }
+
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE,
+                    activeDuringMaintenance ? 1 : 0);
+            onSettingsChanged();
+        }
+    }
+
+    void forceActive(boolean active) {
+        synchronized (mLock) {
+            mForceActive = active;
+            updateActiveLocked();
+        }
+    }
+
+    void dump(PrintWriter pw) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+
+        ipw.println();
+        ipw.println("Low Power Standby Controller:");
+        ipw.increaseIndent();
+        synchronized (mLock) {
+            ipw.print("mIsActive=");
+            ipw.println(mIsActive);
+            ipw.print("mIsEnabled=");
+            ipw.println(mIsEnabled);
+            ipw.print("mSupportedConfig=");
+            ipw.println(mSupportedConfig);
+            ipw.print("mEnabledByDefaultConfig=");
+            ipw.println(mEnabledByDefaultConfig);
+            ipw.print("mStandbyTimeoutConfig=");
+            ipw.println(mStandbyTimeoutConfig);
+
+            if (mIsActive || mIsEnabled) {
+                ipw.print("mIsInteractive=");
+                ipw.println(mIsInteractive);
+                ipw.print("mLastInteractiveTime=");
+                ipw.println(mLastInteractiveTimeElapsed);
+                ipw.print("mIdleSinceNonInteractive=");
+                ipw.println(mIdleSinceNonInteractive);
+                ipw.print("mIsDeviceIdle=");
+                ipw.println(mIsDeviceIdle);
+            }
+
+            final int[] allowlistUids = getAllowlistUidsLocked();
+            ipw.print("mAllowlistUids=");
+            ipw.println(Arrays.toString(allowlistUids));
+        }
+        ipw.decreaseIndent();
+    }
+
+    void dumpProto(ProtoOutputStream proto, long tag) {
+        synchronized (mLock) {
+            final long token = proto.start(tag);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_ACTIVE, mIsActive);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_ENABLED, mIsEnabled);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_SUPPORTED_CONFIG, mSupportedConfig);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_ENABLED_BY_DEFAULT_CONFIG,
+                    mEnabledByDefaultConfig);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_INTERACTIVE, mIsInteractive);
+            proto.write(LowPowerStandbyControllerDumpProto.LAST_INTERACTIVE_TIME,
+                    mLastInteractiveTimeElapsed);
+            proto.write(LowPowerStandbyControllerDumpProto.STANDBY_TIMEOUT_CONFIG,
+                    mStandbyTimeoutConfig);
+            proto.write(LowPowerStandbyControllerDumpProto.IDLE_SINCE_NON_INTERACTIVE,
+                    mIdleSinceNonInteractive);
+            proto.write(LowPowerStandbyControllerDumpProto.IS_DEVICE_IDLE, mIsDeviceIdle);
+
+            final int[] allowlistUids = getAllowlistUidsLocked();
+            for (int appId : allowlistUids) {
+                proto.write(LowPowerStandbyControllerDumpProto.ALLOWLIST, appId);
+            }
+
+            proto.end(token);
+        }
+    }
+
+    private class LowPowerStandbyHandler extends Handler {
+        LowPowerStandbyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_STANDBY_TIMEOUT:
+                    onStandbyTimeoutExpired();
+                    break;
+                case MSG_NOTIFY_ACTIVE_CHANGED:
+                    boolean active = (boolean) msg.obj;
+                    notifyActiveChanged(active);
+                    break;
+                case MSG_NOTIFY_ALLOWLIST_CHANGED:
+                    final int[] allowlistUids = (int[]) msg.obj;
+                    notifyAllowlistChanged(allowlistUids);
+                    break;
+            }
+        }
+    }
+
+    private void addToAllowlistInternal(int uid) {
+        if (DEBUG) {
+            Slog.i(TAG, "Adding to allowlist: " + uid);
+        }
+        synchronized (mLock) {
+            if (mSupportedConfig && !mAllowlistUids.get(uid)) {
+                mAllowlistUids.append(uid, true);
+                enqueueNotifyAllowlistChangedLocked();
+            }
+        }
+    }
+
+    private void removeFromAllowlistInternal(int uid) {
+        if (DEBUG) {
+            Slog.i(TAG, "Removing from allowlist: " + uid);
+        }
+        synchronized (mLock) {
+            if (mSupportedConfig && mAllowlistUids.get(uid)) {
+                mAllowlistUids.delete(uid);
+                enqueueNotifyAllowlistChangedLocked();
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private int[] getAllowlistUidsLocked() {
+        final int[] uids = new int[mAllowlistUids.size()];
+        for (int i = 0; i < mAllowlistUids.size(); i++) {
+            uids[i] = mAllowlistUids.keyAt(i);
+        }
+        return uids;
+    }
+
+    @GuardedBy("mLock")
+    private void enqueueNotifyAllowlistChangedLocked() {
+        final long now = mClock.elapsedRealtime();
+        final int[] allowlistUids = getAllowlistUidsLocked();
+        final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ALLOWLIST_CHANGED, allowlistUids);
+        mHandler.sendMessageAtTime(msg, now);
+    }
+
+    private void notifyAllowlistChanged(int[] allowlistUids) {
+        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+        pmi.setLowPowerStandbyAllowlist(allowlistUids);
+    }
+
+    private final class LocalService extends LowPowerStandbyControllerInternal {
+        @Override
+        public void addToAllowlist(int uid) {
+            addToAllowlistInternal(uid);
+        }
+
+        @Override
+        public void removeFromAllowlist(int uid) {
+            removeFromAllowlistInternal(uid);
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            onSettingsChanged();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java b/services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java
new file mode 100644
index 0000000..f6953fa
--- /dev/null
+++ b/services/core/java/com/android/server/power/LowPowerStandbyControllerInternal.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class LowPowerStandbyControllerInternal {
+    /**
+     * Adds an application to the Low Power Standby allowlist,
+     * exempting it from Low Power Standby restrictions.
+     *
+     * @param uid UID to add to allowlist.
+     */
+    public abstract void addToAllowlist(int uid);
+
+    /**
+     * Removes an application from the Low Power Standby allowlist.
+     *
+     * @param uid UID to remove from allowlist.
+     */
+    public abstract void removeFromAllowlist(int uid);
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4185b2d9..efcfbdd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -37,6 +37,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.SynchronousUserSwitchObserver;
@@ -276,6 +277,7 @@
     private final BatterySaverPolicy mBatterySaverPolicy;
     private final BatterySaverStateMachine mBatterySaverStateMachine;
     private final BatterySavingStats mBatterySavingStats;
+    private final LowPowerStandbyController mLowPowerStandbyController;
     private final AttentionDetector mAttentionDetector;
     private final FaceDownDetector mFaceDownDetector;
     private final ScreenUndimDetector mScreenUndimDetector;
@@ -609,12 +611,17 @@
     // True if we are currently in light device idle mode.
     private boolean mLightDeviceIdleMode;
 
-    // Set of app ids that we will always respect the wake locks for.
+    // Set of app ids that we will respect the wake locks for while in device idle mode.
     int[] mDeviceIdleWhitelist = new int[0];
 
     // Set of app ids that are temporarily allowed to acquire wakelocks due to high-pri message
     int[] mDeviceIdleTempWhitelist = new int[0];
 
+    // Set of app ids that are allowed to acquire wakelocks while low power standby is active
+    int[] mLowPowerStandbyAllowlist = new int[0];
+
+    private boolean mLowPowerStandbyActive;
+
     private final SparseArray<UidState> mUidState = new SparseArray<>();
 
     // A mapping from DisplayGroup Id to PowerGroup. There is a 1-1 mapping between DisplayGroups
@@ -966,6 +973,10 @@
         void invalidateIsInteractiveCaches() {
             PowerManager.invalidateIsInteractiveCaches();
         }
+
+        LowPowerStandbyController createLowPowerStandbyController(Context context, Looper looper) {
+            return new LowPowerStandbyController(context, looper, SystemClock::elapsedRealtime);
+        }
     }
 
     final Constants mConstants;
@@ -1015,6 +1026,8 @@
         mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
                 mBatterySaverController);
 
+        mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext,
+                Looper.getMainLooper());
         mInattentiveSleepWarningOverlayController =
                 mInjector.createInattentiveSleepWarningController();
 
@@ -1228,6 +1241,8 @@
                 // Shouldn't happen since in-process.
             }
 
+            mLowPowerStandbyController.systemReady();
+
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -1654,6 +1669,16 @@
     }
 
     @GuardedBy("mLock")
+    @VisibleForTesting
+    WakeLock findWakeLockLocked(IBinder lock) {
+        int index = findWakeLockIndexLocked(lock);
+        if (index == -1) {
+            return null;
+        }
+        return mWakeLocks.get(index);
+    }
+
+    @GuardedBy("mLock")
     private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
         if (mSystemReady && !wakeLock.mDisabled) {
             wakeLock.mNotifiedAcquired = true;
@@ -3852,6 +3877,24 @@
         }
     }
 
+    void setLowPowerStandbyAllowlistInternal(int[] appids) {
+        synchronized (mLock) {
+            mLowPowerStandbyAllowlist = appids;
+            if (mLowPowerStandbyActive) {
+                updateWakeLockDisabledStatesLocked();
+            }
+        }
+    }
+
+    void setLowPowerStandbyActiveInternal(boolean active) {
+        synchronized (mLock) {
+            if (mLowPowerStandbyActive != active) {
+                mLowPowerStandbyActive = active;
+                updateWakeLockDisabledStatesLocked();
+            }
+        }
+    }
+
     void startUidChangesInternal() {
         synchronized (mLock) {
             mUidsChanging = true;
@@ -3888,7 +3931,7 @@
                     <= ActivityManager.PROCESS_STATE_RECEIVER;
             state.mProcState = procState;
             if (state.mNumWakeLocks > 0) {
-                if (mDeviceIdleMode) {
+                if (mDeviceIdleMode || mLowPowerStandbyActive) {
                     handleUidStateChangeLocked();
                 } else if (!state.mActive && oldShouldAllow !=
                         (procState <= ActivityManager.PROCESS_STATE_RECEIVER)) {
@@ -3908,7 +3951,7 @@
                 state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                 state.mActive = false;
                 mUidState.removeAt(index);
-                if (mDeviceIdleMode && state.mNumWakeLocks > 0) {
+                if ((mDeviceIdleMode || mLowPowerStandbyActive) && state.mNumWakeLocks > 0) {
                     handleUidStateChangeLocked();
                 }
             }
@@ -3993,6 +4036,14 @@
                         disabled = true;
                     }
                 }
+                if (mLowPowerStandbyActive) {
+                    final UidState state = wakeLock.mUidState;
+                    if (Arrays.binarySearch(mLowPowerStandbyAllowlist, appid) < 0
+                            && state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+                            && state.mProcState > ActivityManager.PROCESS_STATE_BOUND_TOP) {
+                        disabled = true;
+                    }
+                }
             }
             if (wakeLock.mDisabled != disabled) {
                 wakeLock.mDisabled = disabled;
@@ -4260,6 +4311,7 @@
         pw.println("POWER MANAGER (dumpsys power)\n");
 
         final WirelessChargerDetector wcd;
+        final LowPowerStandbyController lowPowerStandbyController;
         synchronized (mLock) {
             pw.println("Power Manager State:");
             mConstants.dump(pw);
@@ -4316,6 +4368,7 @@
             pw.println("  mDeviceIdleMode=" + mDeviceIdleMode);
             pw.println("  mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
             pw.println("  mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
+            pw.println("  mLowPowerStandbyActive=" + mLowPowerStandbyActive);
             pw.println("  mLastWakeTime=" + TimeUtils.formatUptime(mLastGlobalWakeTime));
             pw.println("  mLastSleepTime=" + TimeUtils.formatUptime(mLastGlobalSleepTime));
             pw.println("  mLastSleepReason=" + PowerManager.sleepReasonToString(
@@ -4491,10 +4544,13 @@
         mFaceDownDetector.dump(pw);
 
         mAmbientDisplaySuppressionController.dump(pw);
+
+        mLowPowerStandbyController.dump(pw);
     }
 
     private void dumpProto(FileDescriptor fd) {
         final WirelessChargerDetector wcd;
+        final LowPowerStandbyController lowPowerStandbyController;
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
 
         synchronized (mLock) {
@@ -4599,6 +4655,9 @@
                 proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
             }
 
+            proto.write(PowerManagerServiceDumpProto.IS_LOW_POWER_STANDBY_ACTIVE,
+                    mLowPowerStandbyActive);
+
             proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastGlobalWakeTime);
             proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastGlobalSleepTime);
             proto.write(
@@ -4832,6 +4891,7 @@
             for (SuspendBlocker sb : mSuspendBlockers) {
                 sb.dumpDebug(proto, PowerManagerServiceDumpProto.SUSPEND_BLOCKERS);
             }
+
             wcd = mWirelessChargerDetector;
         }
 
@@ -4839,6 +4899,9 @@
             wcd.dumpDebug(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
         }
 
+        mLowPowerStandbyController.dumpProto(proto,
+                PowerManagerServiceDumpProto.LOW_POWER_STANDBY_CONTROLLER);
+
         proto.flush();
     }
 
@@ -5092,6 +5155,8 @@
                     (mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP)!=0);
             proto.write(WakeLockProto.WakeLockFlagsProto.IS_ON_AFTER_RELEASE,
                     (mFlags & PowerManager.ON_AFTER_RELEASE)!=0);
+            proto.write(WakeLockProto.WakeLockFlagsProto.SYSTEM_WAKELOCK,
+                    (mFlags & PowerManager.SYSTEM_WAKELOCK) != 0);
             proto.end(wakeLockFlagsToken);
 
             proto.write(WakeLockProto.IS_DISABLED, mDisabled);
@@ -5138,6 +5203,9 @@
             if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
                 result += " ON_AFTER_RELEASE";
             }
+            if ((mFlags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+                result += " SYSTEM_WAKELOCK";
+            }
             return result;
         }
     }
@@ -5299,8 +5367,22 @@
                 ws = null;
             }
 
-            final int uid = Binder.getCallingUid();
-            final int pid = Binder.getCallingPid();
+            int uid = Binder.getCallingUid();
+            int pid = Binder.getCallingPid();
+
+            if ((flags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                        null);
+                WorkSource workSource = new WorkSource(Binder.getCallingUid(), packageName);
+                if (ws != null && !ws.isEmpty()) {
+                    workSource.add(ws);
+                }
+                ws = workSource;
+
+                uid = Process.myUid();
+                pid = Process.myPid();
+            }
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
@@ -5768,6 +5850,100 @@
             }
         }
 
+        @Override // Binder call
+        @RequiresPermission(anyOf = {
+                android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                android.Manifest.permission.DEVICE_POWER
+        })
+        public boolean isLowPowerStandbySupported() {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                        "isLowPowerStandbySupported");
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mLowPowerStandbyController.isSupported();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        public boolean isLowPowerStandbyEnabled() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mLowPowerStandbyController.isEnabled();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        @RequiresPermission(anyOf = {
+                android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                android.Manifest.permission.DEVICE_POWER
+        })
+        public void setLowPowerStandbyEnabled(boolean enabled) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                        "setLowPowerStandbyEnabled");
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mLowPowerStandbyController.setEnabled(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        @RequiresPermission(anyOf = {
+                android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                android.Manifest.permission.DEVICE_POWER
+        })
+        public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                        "setLowPowerStandbyActiveDuringMaintenance");
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mLowPowerStandbyController.setActiveDuringMaintenance(activeDuringMaintenance);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        @RequiresPermission(anyOf = {
+                android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                android.Manifest.permission.DEVICE_POWER
+        })
+        public void forceLowPowerStandbyActive(boolean active) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
+                        "forceLowPowerStandbyActive");
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mLowPowerStandbyController.forceActive(active);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         /**
          * Gets the reason for the last time the phone had to reboot.
          *
@@ -6249,6 +6425,16 @@
         }
 
         @Override
+        public void setLowPowerStandbyAllowlist(int[] appids) {
+            setLowPowerStandbyAllowlistInternal(appids);
+        }
+
+        @Override
+        public void setLowPowerStandbyActive(boolean enabled) {
+            setLowPowerStandbyActiveInternal(enabled);
+        }
+
+        @Override
         public void startUidChanges() {
             startUidChangesInternal();
         }
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index 88c9850..d20c7f1 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -106,6 +106,7 @@
      */
     private static final int FLAG_ON_AFTER_RELEASE = 0x8;
     private static final int FLAG_ACQUIRE_CAUSES_WAKEUP = 0x10;
+    private static final int FLAG_SYSTEM_WAKELOCK = 0x20;
 
     private static final int MASK_LOWER_6_BITS = 0x3F;
     private static final int MASK_LOWER_7_BITS = 0x7F;
@@ -296,6 +297,9 @@
         if ((flags & PowerManager.ON_AFTER_RELEASE) != 0) {
             newFlags |= FLAG_ON_AFTER_RELEASE;
         }
+        if ((flags & PowerManager.SYSTEM_WAKELOCK) != 0) {
+            newFlags |= FLAG_SYSTEM_WAKELOCK;
+        }
         return newFlags;
     }
 
@@ -455,6 +459,9 @@
             if ((flags & FLAG_ACQUIRE_CAUSES_WAKEUP) == FLAG_ACQUIRE_CAUSES_WAKEUP) {
                 sb.append(",acq-causes-wake");
             }
+            if ((flags & FLAG_SYSTEM_WAKELOCK) == FLAG_SYSTEM_WAKELOCK) {
+                sb.append(",system-wakelock");
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java b/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java
new file mode 100644
index 0000000..f797f09
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/AllSensorStateController.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.annotation.NonNull;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.IoThread;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+
+class AllSensorStateController {
+
+    private static final String LOG_TAG = AllSensorStateController.class.getSimpleName();
+
+    private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
+    private static final String XML_TAG_SENSOR_PRIVACY = "all-sensor-privacy";
+    private static final String XML_TAG_SENSOR_PRIVACY_LEGACY = "sensor-privacy";
+    private static final String XML_ATTRIBUTE_ENABLED = "enabled";
+
+    private static AllSensorStateController sInstance;
+
+    private final AtomicFile mAtomicFile =
+            new AtomicFile(new File(Environment.getDataSystemDirectory(), SENSOR_PRIVACY_XML_FILE));
+
+    private boolean mEnabled;
+    private SensorPrivacyStateController.AllSensorPrivacyListener mListener;
+    private Handler mListenerHandler;
+
+    static AllSensorStateController getInstance() {
+        if (sInstance == null) {
+            sInstance = new AllSensorStateController();
+        }
+        return sInstance;
+    }
+
+    private AllSensorStateController() {
+        if (!mAtomicFile.exists()) {
+            return;
+        }
+        try (FileInputStream inputStream = mAtomicFile.openRead()) {
+            TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                String tagName = parser.getName();
+                if (XML_TAG_SENSOR_PRIVACY.equals(tagName)) {
+                    mEnabled |= XmlUtils
+                            .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
+                    break;
+                }
+                if (XML_TAG_SENSOR_PRIVACY_LEGACY.equals(tagName)) {
+                    mEnabled |= XmlUtils
+                            .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
+                }
+                if ("user".equals(tagName)) { // Migrate from mic/cam toggles format
+                    int user = XmlUtils.readIntAttribute(parser, "id", -1);
+                    if (user == 0) {
+                        mEnabled |=
+                                XmlUtils.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED);
+                    }
+                }
+                XmlUtils.nextElement(parser);
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+            mEnabled = false;
+        }
+    }
+
+    public boolean getAllSensorStateLocked() {
+        return mEnabled;
+    }
+
+    public void setAllSensorStateLocked(boolean enabled) {
+        if (mEnabled != enabled) {
+            mEnabled = enabled;
+            if (mListener != null && mListenerHandler != null) {
+                mListenerHandler.sendMessage(
+                        PooledLambda.obtainMessage(mListener::onAllSensorPrivacyChanged, enabled));
+            }
+        }
+    }
+
+    void setAllSensorPrivacyListenerLocked(Handler handler,
+            SensorPrivacyStateController.AllSensorPrivacyListener listener) {
+        Objects.requireNonNull(handler);
+        Objects.requireNonNull(listener);
+        if (mListener != null) {
+            throw new IllegalStateException("Listener is already set");
+        }
+        mListener = listener;
+        mListenerHandler = handler;
+    }
+
+    public void schedulePersistLocked() {
+        IoThread.getHandler().sendMessage(PooledLambda.obtainMessage(this::persist, mEnabled));
+    }
+
+    private void persist(boolean enabled) {
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = mAtomicFile.startWrite();
+            TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+            serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
+            serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+            serializer.endDocument();
+            mAtomicFile.finishWrite(outputStream);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
+            mAtomicFile.failWrite(outputStream);
+        }
+    }
+
+    void resetForTesting() {
+        mListener = null;
+        mListenerHandler = null;
+        mEnabled = false;
+    }
+
+    void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
+        // TODO stub
+    }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/OWNERS b/services/core/java/com/android/server/sensorprivacy/OWNERS
new file mode 100644
index 0000000..15e3f7a
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/libs/sensorprivacy/OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
new file mode 100644
index 0000000..ce9fff5
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.service.SensorPrivacyIndividualEnabledSensorProto;
+import android.service.SensorPrivacySensorProto;
+import android.service.SensorPrivacyServiceDumpProto;
+import android.service.SensorPrivacyUserProto;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.QuadConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Class for managing persisted state. Synchronization must be handled by the caller.
+ */
+class PersistedState {
+
+    private static final String LOG_TAG = PersistedState.class.getSimpleName();
+
+    /** Version number indicating compatibility parsing the persisted file */
+    private static final int CURRENT_PERSISTENCE_VERSION = 2;
+    /** Version number indicating the persisted data needs upgraded to match new internal data
+     *  structures and features */
+    private static final int CURRENT_VERSION = 2;
+
+    private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
+    private static final String XML_TAG_SENSOR_STATE = "sensor-state";
+    private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
+    private static final String XML_ATTRIBUTE_VERSION = "version";
+    private static final String XML_ATTRIBUTE_TOGGLE_TYPE = "toggle-type";
+    private static final String XML_ATTRIBUTE_USER_ID = "user-id";
+    private static final String XML_ATTRIBUTE_SENSOR = "sensor";
+    private static final String XML_ATTRIBUTE_STATE_TYPE = "state-type";
+    private static final String XML_ATTRIBUTE_LAST_CHANGE = "last-change";
+
+    private final AtomicFile mAtomicFile;
+
+    private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
+
+    static PersistedState fromFile(String fileName) {
+        return new PersistedState(fileName);
+    }
+
+    private PersistedState(String fileName) {
+        mAtomicFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
+        readState();
+    }
+
+    private void readState() {
+        AtomicFile file = mAtomicFile;
+        if (!file.exists()) {
+            AtomicFile fileToMigrateFrom =
+                    new AtomicFile(new File(Environment.getDataSystemDirectory(),
+                            "sensor_privacy.xml"));
+
+            if (fileToMigrateFrom.exists()) {
+                // Sample the start tag to determine if migration is needed
+                try (FileInputStream inputStream = fileToMigrateFrom.openRead()) {
+                    TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+                    XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+                    file = fileToMigrateFrom;
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+                    // Delete the file to prevent the same error on subsequent calls and assume
+                    // sensor privacy is not enabled.
+                    fileToMigrateFrom.delete();
+                } catch (XmlPullParserException e) {
+                    // No migration needed
+                }
+            }
+        }
+
+        Object nonupgradedState = null;
+        if (file.exists()) {
+            try (FileInputStream inputStream = file.openRead()) {
+                TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
+                XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
+                final int persistenceVersion = parser.getAttributeInt(null,
+                        XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
+
+                // Use inline string literals for xml tags/attrs when parsing old versions since
+                // these should never be changed even with refactorings.
+                if (persistenceVersion == 0) {
+                    int version = 0;
+                    PVersion0 version0 = new PVersion0(version);
+                    nonupgradedState = version0;
+                    readPVersion0(parser, version0);
+                } else if (persistenceVersion == 1) {
+                    int version = parser.getAttributeInt(null,
+                            "version", 1);
+                    PVersion1 version1 = new PVersion1(version);
+                    nonupgradedState = version1;
+
+                    readPVersion1(parser, version1);
+                } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
+                    int version = parser.getAttributeInt(null,
+                            XML_ATTRIBUTE_VERSION, 2);
+                    PVersion2 version2 = new PVersion2(version);
+                    nonupgradedState = version2;
+
+                    readPVersion2(parser, version2);
+                } else {
+                    Log.e(LOG_TAG, "Unknown persistence version: " + persistenceVersion
+                                    + ". Deleting.",
+                            new RuntimeException());
+                    file.delete();
+                    nonupgradedState = null;
+                }
+
+            } catch (IOException | XmlPullParserException | RuntimeException e) {
+                Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
+                // Delete the file to prevent the same error on subsequent calls and assume
+                // sensor privacy is not enabled.
+                file.delete();
+                nonupgradedState = null;
+            }
+        }
+
+        if (nonupgradedState == null) {
+            // New file, default state for current version goes here.
+            nonupgradedState = new PVersion2(2);
+        }
+
+        if (nonupgradedState instanceof PVersion0) {
+            nonupgradedState = PVersion1.fromPVersion0((PVersion0) nonupgradedState);
+        }
+        if (nonupgradedState instanceof PVersion1) {
+            nonupgradedState = PVersion2.fromPVersion1((PVersion1) nonupgradedState);
+        }
+        if (nonupgradedState instanceof PVersion2) {
+            PVersion2 upgradedState = (PVersion2) nonupgradedState;
+            mStates = upgradedState.mStates;
+        } else {
+            Log.e(LOG_TAG, "State not successfully upgraded.");
+            mStates = new ArrayMap<>();
+        }
+    }
+
+    private static void readPVersion0(TypedXmlPullParser parser, PVersion0 version0)
+            throws XmlPullParserException, IOException {
+
+        XmlUtils.nextElement(parser);
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            if ("individual-sensor-privacy".equals(parser.getName())) {
+                int sensor = XmlUtils.readIntAttribute(parser, "sensor");
+                boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
+                        "enabled");
+                version0.addState(sensor, indEnabled);
+                XmlUtils.skipCurrentTag(parser);
+            } else {
+                XmlUtils.nextElement(parser);
+            }
+        }
+    }
+
+    private static void readPVersion1(TypedXmlPullParser parser, PVersion1 version1)
+            throws XmlPullParserException, IOException {
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            XmlUtils.nextElement(parser);
+
+            if ("user".equals(parser.getName())) {
+                int currentUserId = parser.getAttributeInt(null, "id");
+                int depth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, depth)) {
+                    if ("individual-sensor-privacy".equals(parser.getName())) {
+                        int sensor = parser.getAttributeInt(null, "sensor");
+                        boolean isEnabled = parser.getAttributeBoolean(null,
+                                "enabled");
+                        version1.addState(currentUserId, sensor, isEnabled);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void readPVersion2(TypedXmlPullParser parser, PVersion2 version2)
+            throws XmlPullParserException, IOException {
+
+        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+            XmlUtils.nextElement(parser);
+
+            if (XML_TAG_SENSOR_STATE.equals(parser.getName())) {
+                int toggleType = parser.getAttributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE);
+                int userId = parser.getAttributeInt(null, XML_ATTRIBUTE_USER_ID);
+                int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
+                int state = parser.getAttributeInt(null, XML_ATTRIBUTE_STATE_TYPE);
+                long lastChange = parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CHANGE);
+
+                version2.addState(toggleType, userId, sensor, state, lastChange);
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
+    public SensorState getState(int toggleType, int userId, int sensor) {
+        return mStates.get(new TypeUserSensor(toggleType, userId, sensor));
+    }
+
+    public SensorState setState(int toggleType, int userId, int sensor, SensorState sensorState) {
+        return mStates.put(new TypeUserSensor(toggleType, userId, sensor), sensorState);
+    }
+
+    private static class TypeUserSensor {
+
+        int mType;
+        int mUserId;
+        int mSensor;
+
+        TypeUserSensor(int type, int userId, int sensor) {
+            mType = type;
+            mUserId = userId;
+            mSensor = sensor;
+        }
+
+        TypeUserSensor(TypeUserSensor typeUserSensor) {
+            this(typeUserSensor.mType, typeUserSensor.mUserId, typeUserSensor.mSensor);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof TypeUserSensor)) return false;
+            TypeUserSensor that = (TypeUserSensor) o;
+            return mType == that.mType && mUserId == that.mUserId && mSensor == that.mSensor;
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * (31 * mType + mUserId) + mSensor;
+        }
+    }
+
+    void schedulePersist() {
+        int numStates = mStates.size();
+
+        ArrayMap<TypeUserSensor, SensorState> statesCopy = new ArrayMap<>();
+        for (int i = 0; i < numStates; i++) {
+            statesCopy.put(new TypeUserSensor(mStates.keyAt(i)),
+                    new SensorState(mStates.valueAt(i)));
+        }
+        IoThread.getHandler().sendMessage(
+                PooledLambda.obtainMessage(PersistedState::persist, this, statesCopy));
+    }
+
+    private void persist(ArrayMap<TypeUserSensor, SensorState> states) {
+        FileOutputStream outputStream = null;
+        try {
+            outputStream = mAtomicFile.startWrite();
+            TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
+            serializer.attributeInt(null, XML_ATTRIBUTE_PERSISTENCE_VERSION,
+                    CURRENT_PERSISTENCE_VERSION);
+            serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
+            for (int i = 0; i < states.size(); i++) {
+                TypeUserSensor userSensor = states.keyAt(i);
+                SensorState sensorState = states.valueAt(i);
+
+                serializer.startTag(null, XML_TAG_SENSOR_STATE);
+                serializer.attributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE,
+                        userSensor.mType);
+                serializer.attributeInt(null, XML_ATTRIBUTE_USER_ID,
+                        userSensor.mUserId);
+                serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR,
+                        userSensor.mSensor);
+                serializer.attributeInt(null, XML_ATTRIBUTE_STATE_TYPE,
+                        sensorState.getState());
+                serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CHANGE,
+                        sensorState.getLastChange());
+                serializer.endTag(null, XML_TAG_SENSOR_STATE);
+            }
+
+            serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
+            serializer.endDocument();
+            mAtomicFile.finishWrite(outputStream);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
+            mAtomicFile.failWrite(outputStream);
+        }
+    }
+
+    void dump(DualDumpOutputStream dumpStream) {
+        // Collect per user, then per sensor. <toggle type, state>
+        SparseArray<SparseArray<Pair<Integer, SensorState>>> statesMatrix = new SparseArray<>();
+        int numStates = mStates.size();
+        for (int i = 0; i < numStates; i++) {
+            int toggleType = mStates.keyAt(i).mType;
+            int userId = mStates.keyAt(i).mUserId;
+            int sensor = mStates.keyAt(i).mSensor;
+
+            SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.get(userId);
+            if (userStates == null) {
+                userStates = new SparseArray<>();
+                statesMatrix.put(userId, userStates);
+            }
+            userStates.put(sensor, new Pair<>(toggleType, mStates.valueAt(i)));
+        }
+
+        dumpStream.write("storage_implementation",
+                SensorPrivacyServiceDumpProto.STORAGE_IMPLEMENTATION,
+                SensorPrivacyStateControllerImpl.class.getName());
+
+        int numUsers = statesMatrix.size();
+        for (int i = 0; i < numUsers; i++) {
+            int userId = statesMatrix.keyAt(i);
+            long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
+            dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
+            SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.valueAt(i);
+            int numSensors = userStates.size();
+            for (int j = 0; j < numSensors; j++) {
+                int sensor = userStates.keyAt(j);
+                int toggleType = userStates.valueAt(j).first;
+                SensorState sensorState = userStates.valueAt(j).second;
+                long sensorToken = dumpStream.start("sensors", SensorPrivacyUserProto.SENSORS);
+                dumpStream.write("sensor", SensorPrivacySensorProto.SENSOR, sensor);
+                long toggleToken = dumpStream.start("toggles", SensorPrivacySensorProto.TOGGLES);
+                dumpStream.write("toggle_type",
+                        SensorPrivacyIndividualEnabledSensorProto.TOGGLE_TYPE,
+                        toggleType);
+                dumpStream.write("state_type",
+                        SensorPrivacyIndividualEnabledSensorProto.STATE_TYPE,
+                        sensorState.getState());
+                dumpStream.write("last_change",
+                        SensorPrivacyIndividualEnabledSensorProto.LAST_CHANGE,
+                        sensorState.getLastChange());
+                dumpStream.end(toggleToken);
+                dumpStream.end(sensorToken);
+            }
+            dumpStream.end(userToken);
+        }
+    }
+
+    void forEachKnownState(QuadConsumer<Integer, Integer, Integer, SensorState> consumer) {
+        int numStates = mStates.size();
+        for (int i = 0; i < numStates; i++) {
+            TypeUserSensor tus = mStates.keyAt(i);
+            SensorState sensorState = mStates.valueAt(i);
+            consumer.accept(tus.mType, tus.mUserId, tus.mSensor, sensorState);
+        }
+    }
+
+    // Structure for persistence version 0
+    private static class PVersion0 {
+        private SparseArray<SensorState> mIndividualEnabled = new SparseArray<>();
+
+        private PVersion0(int version) {
+            if (version != 0) {
+                throw new RuntimeException("Only version 0 supported");
+            }
+        }
+
+        private void addState(int sensor, boolean enabled) {
+            mIndividualEnabled.put(sensor, new SensorState(enabled));
+        }
+
+        private void upgrade() {
+            // No op, only version 0 is supported
+        }
+    }
+
+    // Structure for persistence version 1
+    private static class PVersion1 {
+        private SparseArray<SparseArray<SensorState>> mIndividualEnabled = new SparseArray<>();
+
+        private PVersion1(int version) {
+            if (version != 1) {
+                throw new RuntimeException("Only version 1 supported");
+            }
+        }
+
+        private static PVersion1 fromPVersion0(PVersion0 version0) {
+            version0.upgrade();
+
+            PVersion1 result = new PVersion1(1);
+
+            int[] users = {UserHandle.USER_SYSTEM};
+            try {
+                users = LocalServices.getService(UserManagerInternal.class).getUserIds();
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Unable to get users.", e);
+            }
+
+            // Copy global state to each user
+            for (int i = 0; i < users.length; i++) {
+                int userId = users[i];
+
+                for (int j = 0; j < version0.mIndividualEnabled.size(); j++) {
+                    final int sensor = version0.mIndividualEnabled.keyAt(j);
+                    final SensorState sensorState = version0.mIndividualEnabled.valueAt(j);
+
+                    result.addState(userId, sensor, sensorState.isEnabled());
+                }
+            }
+
+            return result;
+        }
+
+        private void addState(int userId, int sensor, boolean enabled) {
+            SparseArray<SensorState> userIndividualSensorEnabled =
+                    mIndividualEnabled.get(userId, new SparseArray<>());
+            mIndividualEnabled.put(userId, userIndividualSensorEnabled);
+
+            userIndividualSensorEnabled
+                    .put(sensor, new SensorState(enabled));
+        }
+
+        private void upgrade() {
+            // No op, only version 1 is supported
+        }
+    }
+
+    // Structure for persistence version 2
+    private static class PVersion2 {
+        private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
+
+        private PVersion2(int version) {
+            if (version != 2) {
+                throw new RuntimeException("Only version 2 supported");
+            }
+        }
+
+        private static PVersion2 fromPVersion1(PVersion1 version1) {
+            version1.upgrade();
+
+            PVersion2 result = new PVersion2(2);
+
+            SparseArray<SparseArray<SensorState>> individualEnabled =
+                    version1.mIndividualEnabled;
+            int numUsers = individualEnabled.size();
+            for (int i = 0; i < numUsers; i++) {
+                int userId = individualEnabled.keyAt(i);
+                SparseArray<SensorState> userIndividualEnabled = individualEnabled.valueAt(i);
+                int numSensors = userIndividualEnabled.size();
+                for (int j = 0; j < numSensors; j++) {
+                    int sensor = userIndividualEnabled.keyAt(j);
+                    SensorState sensorState = userIndividualEnabled.valueAt(j);
+                    result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+                            userId, sensor, sensorState.getState(), sensorState.getLastChange());
+                }
+            }
+
+            return result;
+        }
+
+        private void addState(int toggleType, int userId, int sensor, int state,
+                long lastChange) {
+            mStates.put(new TypeUserSensor(toggleType, userId, sensor),
+                    new SensorState(state, lastChange));
+        }
+    }
+
+    public void resetForTesting() {
+        mStates = new ArrayMap<>();
+    }
+}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
similarity index 71%
rename from services/core/java/com/android/server/SensorPrivacyService.java
rename to services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 7a4d11c..e9b5f11 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.sensorprivacy;
 
 import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
@@ -40,10 +40,10 @@
 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
 import static android.hardware.SensorPrivacyManager.Sources.SHELL;
 import static android.os.UserHandle.USER_NULL;
-import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA;
@@ -83,7 +83,6 @@
 import android.hardware.SensorPrivacyManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -97,26 +96,19 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.service.SensorPrivacyIndividualEnabledSensorProto;
-import android.service.SensorPrivacyServiceDumpProto;
-import android.service.SensorPrivacyUserProto;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.text.Html;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -125,19 +117,14 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FunctionalUtils;
-import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -151,32 +138,10 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_LOGGING = false;
 
-    /** Version number indicating compatibility parsing the persisted file */
-    private static final int CURRENT_PERSISTENCE_VERSION = 1;
-    /** Version number indicating the persisted data needs upgraded to match new internal data
-     *  structures and features */
-    private static final int CURRENT_VERSION = 1;
-
-    private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
-    private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
-    private static final String XML_TAG_USER = "user";
-    private static final String XML_TAG_INDIVIDUAL_SENSOR_PRIVACY = "individual-sensor-privacy";
-    private static final String XML_ATTRIBUTE_ID = "id";
-    private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
-    private static final String XML_ATTRIBUTE_VERSION = "version";
-    private static final String XML_ATTRIBUTE_ENABLED = "enabled";
-    private static final String XML_ATTRIBUTE_LAST_CHANGE = "last-change";
-    private static final String XML_ATTRIBUTE_SENSOR = "sensor";
-
     private static final String SENSOR_PRIVACY_CHANNEL_ID = Context.SENSOR_PRIVACY_SERVICE;
     private static final String ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY =
             SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy";
 
-    // These are associated with fields that existed for older persisted versions of files
-    private static final int VER0_ENABLED = 0;
-    private static final int VER0_INDIVIDUAL_ENABLED = 1;
-    private static final int VER1_ENABLED = 0;
-    private static final int VER1_INDIVIDUAL_ENABLED = 1;
     public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
 
     private final Context mContext;
@@ -200,6 +165,7 @@
 
     public SensorPrivacyService(Context context) {
         super(context);
+
         mContext = context;
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mAppOpsManagerInternal = getLocalService(AppOpsManagerInternal.class);
@@ -247,12 +213,8 @@
 
         private final SensorPrivacyHandler mHandler;
         private final Object mLock = new Object();
-        @GuardedBy("mLock")
-        private final AtomicFile mAtomicFile;
-        @GuardedBy("mLock")
-        private SparseBooleanArray mEnabled = new SparseBooleanArray();
-        @GuardedBy("mLock")
-        private SparseArray<SparseArray<SensorState>> mIndividualEnabled = new SparseArray<>();
+
+        private SensorPrivacyStateController mSensorPrivacyStateController;
 
         /**
          * Packages for which not to show sensor use reminders.
@@ -266,34 +228,6 @@
         private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>>
                 mQueuedSensorUseReminderDialogs = new ArrayMap<>();
 
-        private class SensorState {
-            private boolean mEnabled;
-            private long mLastChange;
-
-            SensorState(boolean enabled) {
-                mEnabled = enabled;
-                mLastChange = getCurrentTimeMillis();
-            }
-
-            SensorState(boolean enabled, long lastChange) {
-                mEnabled = enabled;
-                if (lastChange < 0) {
-                    mLastChange = getCurrentTimeMillis();
-                } else {
-                    mLastChange = lastChange;
-                }
-            }
-
-            boolean setEnabled(boolean enabled) {
-                if (mEnabled != enabled) {
-                    mEnabled = enabled;
-                    mLastChange = getCurrentTimeMillis();
-                    return true;
-                }
-                return false;
-            }
-        }
-
         private class SensorUseReminderDialogInfo {
             private int mTaskId;
             private UserHandle mUser;
@@ -323,14 +257,7 @@
 
         SensorPrivacyServiceImpl() {
             mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext);
-            File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(),
-                    SENSOR_PRIVACY_XML_FILE);
-            mAtomicFile = new AtomicFile(sensorPrivacyFile);
-            synchronized (mLock) {
-                if (readPersistedSensorPrivacyStateLocked()) {
-                    persistSensorPrivacyStateLocked();
-                }
-            }
+            mSensorPrivacyStateController = SensorPrivacyStateController.getInstance();
 
             int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE,
                     OP_CAMERA, OP_PHONE_CALL_CAMERA};
@@ -349,7 +276,26 @@
                 }
             }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY),
                     MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
+
+            mContext.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mSensorPrivacyStateController.forEachState(
+                            (toggleType, userId, sensor, state) ->
+                                    logSensorPrivacyToggle(OTHER, sensor, state.isEnabled(),
+                                    state.getLastChange(), true)
+                    );
+                }
+            }, new IntentFilter(Intent.ACTION_SHUTDOWN));
+
             mUserManagerInternal.addUserRestrictionsListener(this);
+
+            mSensorPrivacyStateController.setAllSensorPrivacyListener(
+                    mHandler, mHandler::handleSensorPrivacyChanged);
+            mSensorPrivacyStateController.setSensorPrivacyListener(
+                    mHandler,
+                    (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged(
+                            userId, sensor, state.isEnabled()));
         }
 
         @Override
@@ -490,8 +436,9 @@
                 }
             }
 
-            String inputMethodComponent = Settings.Secure.getString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD);
+            String inputMethodComponent = Settings.Secure.getStringForUser(
+                    mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
+                    mCurrentUser);
             String inputMethodPackageName = null;
             if (inputMethodComponent != null) {
                 inputMethodPackageName = ComponentName.unflattenFromString(
@@ -546,7 +493,8 @@
         private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user,
                 @NonNull String packageName, int sensor) {
             mHandler.sendMessage(PooledLambda.obtainMessage(
-                    this:: enqueueSensorUseReminderDialog, taskId, user, packageName, sensor));
+                    SensorPrivacyServiceImpl::enqueueSensorUseReminderDialog, this, taskId, user,
+                    packageName, sensor));
         }
 
         private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user,
@@ -564,8 +512,8 @@
                     sensors.add(sensor);
                 }
                 mQueuedSensorUseReminderDialogs.put(info, sensors);
-                mHandler.sendMessageDelayed(
-                        PooledLambda.obtainMessage(this::showSensorUserReminderDialog, info),
+                mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+                        SensorPrivacyServiceImpl::showSensorUserReminderDialog, this, info),
                         REMINDER_DIALOG_DELAY_MILLIS);
                 return;
             }
@@ -655,28 +603,32 @@
             notificationManager.createNotificationChannel(channel);
 
             Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
+
+            String contentTitle = getUiContext().getString(messageRes);
+            Spanned contentText = Html.fromHtml(getUiContext().getString(
+                    R.string.sensor_privacy_start_use_notification_content_text, packageLabel), 0);
+            PendingIntent contentIntent = PendingIntent.getActivity(mContext, sensor,
+                    new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+                    PendingIntent.FLAG_IMMUTABLE
+                            | PendingIntent.FLAG_UPDATE_CURRENT);
+
+            String actionTitle = getUiContext().getString(
+                    R.string.sensor_privacy_start_use_dialog_turn_on_button);
+            PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, sensor,
+                    new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
+                            .setPackage(mContext.getPackageName())
+                            .putExtra(EXTRA_SENSOR, sensor)
+                            .putExtra(Intent.EXTRA_USER, user),
+                    PendingIntent.FLAG_IMMUTABLE
+                            | PendingIntent.FLAG_UPDATE_CURRENT);
             notificationManager.notify(notificationId,
                     new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
-                            .setContentTitle(getUiContext().getString(messageRes))
-                            .setContentText(Html.fromHtml(getUiContext().getString(
-                                    R.string.sensor_privacy_start_use_notification_content_text,
-                                    packageLabel),0))
+                            .setContentTitle(contentTitle)
+                            .setContentText(contentText)
                             .setSmallIcon(icon)
                             .addAction(new Notification.Action.Builder(icon,
-                                    getUiContext().getString(
-                                            R.string.sensor_privacy_start_use_dialog_turn_on_button),
-                                    PendingIntent.getBroadcast(mContext, sensor,
-                                            new Intent(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)
-                                                    .setPackage(mContext.getPackageName())
-                                                    .putExtra(EXTRA_SENSOR, sensor)
-                                                    .putExtra(Intent.EXTRA_USER, user),
-                                            PendingIntent.FLAG_IMMUTABLE
-                                                    | PendingIntent.FLAG_UPDATE_CURRENT))
-                                    .build())
-                            .setContentIntent(PendingIntent.getActivity(mContext, sensor,
-                                    new Intent(Settings.ACTION_PRIVACY_SETTINGS),
-                                    PendingIntent.FLAG_IMMUTABLE
-                                            | PendingIntent.FLAG_UPDATE_CURRENT))
+                                    actionTitle, actionIntent).build())
+                            .setContentIntent(contentIntent)
                             .extend(new Notification.TvExtender())
                             .setTimeoutAfter(isTelevision(mContext)
                                     ? /* dismiss immediately */ 1
@@ -697,16 +649,7 @@
         @Override
         public void setSensorPrivacy(boolean enable) {
             enforceManageSensorPrivacyPermission();
-            // Keep the state consistent between all users to make it a single global state
-            forAllUsers(userId -> setSensorPrivacy(userId, enable));
-        }
-
-        private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
-            synchronized (mLock) {
-                mEnabled.put(userId, enable);
-                persistSensorPrivacyStateLocked();
-            }
-            mHandler.onSensorPrivacyChanged(enable);
+            mSensorPrivacyStateController.setAllSensorState(enable);
         }
 
         @Override
@@ -735,43 +678,23 @@
 
         private void setIndividualSensorPrivacyUnchecked(int userId, int source, int sensor,
                 boolean enable) {
-            synchronized (mLock) {
-                SparseArray<SensorState> userIndividualEnabled = mIndividualEnabled.get(userId,
-                        new SparseArray<>());
-                SensorState sensorState = userIndividualEnabled.get(sensor);
-                long lastChange;
-                if (sensorState != null) {
-                    lastChange = sensorState.mLastChange;
-                    if (!sensorState.setEnabled(enable)) {
-                        // State not changing
-                        return;
-                    }
-                } else {
-                    sensorState = new SensorState(enable);
-                    lastChange = sensorState.mLastChange;
-                    userIndividualEnabled.put(sensor, sensorState);
-                }
-                mIndividualEnabled.put(userId, userIndividualEnabled);
-
-                if (userId == mUserManagerInternal.getProfileParentId(userId)) {
-                    logSensorPrivacyToggle(source, sensor, sensorState.mEnabled, lastChange);
-                }
-
-                if (!enable) {
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        // Remove any notifications prompting the user to disable sensory privacy
-                        NotificationManager notificationManager =
-                                mContext.getSystemService(NotificationManager.class);
-
-                        notificationManager.cancel(sensor);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                }
-                persistSensorPrivacyState();
-            }
-            mHandler.onSensorPrivacyChanged(userId, sensor, enable);
+            final long[] lastChange = new long[1];
+            mSensorPrivacyStateController.atomic(() -> {
+                SensorState sensorState = mSensorPrivacyStateController
+                        .getState(SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor);
+                lastChange[0] = sensorState.getLastChange();
+                mSensorPrivacyStateController.setState(
+                        SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor, enable, mHandler,
+                        changeSuccessful -> {
+                            if (changeSuccessful) {
+                                if (userId == mUserManagerInternal.getProfileParentId(userId)) {
+                                    mHandler.sendMessage(PooledLambda.obtainMessage(
+                                            SensorPrivacyServiceImpl::logSensorPrivacyToggle, this,
+                                            source, sensor, enable, lastChange[0], false));
+                                }
+                            }
+                        });
+            });
         }
 
         private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
@@ -801,14 +724,23 @@
         }
 
         private void logSensorPrivacyToggle(int source, int sensor, boolean enabled,
-                long lastChange) {
+                long lastChange, boolean onShutDown) {
             long logMins = Math.max(0, (getCurrentTimeMillis() - lastChange) / (1000 * 60));
 
             int logAction = -1;
-            if (enabled) {
-                logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
+            if (onShutDown) {
+                // TODO ACTION_POWER_OFF_WHILE_(ON/OFF)
+                if (enabled) {
+                    logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
+                } else {
+                    logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
+                }
             } else {
-                logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+                if (enabled) {
+                    logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
+                } else {
+                    logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+                }
             }
 
             int logSensor = -1;
@@ -895,13 +827,7 @@
         @Override
         public boolean isSensorPrivacyEnabled() {
             enforceObserveSensorPrivacyPermission();
-            return isSensorPrivacyEnabled(USER_SYSTEM);
-        }
-
-        private boolean isSensorPrivacyEnabled(@UserIdInt int userId) {
-            synchronized (mLock) {
-                return mEnabled.get(userId, false);
-            }
+            return mSensorPrivacyStateController.getAllSensorState();
         }
 
         @Override
@@ -918,239 +844,8 @@
             if (userId == UserHandle.USER_CURRENT) {
                 userId = mCurrentUser;
             }
-            synchronized (mLock) {
-                return isIndividualSensorPrivacyEnabledLocked(userId, sensor);
-            }
-        }
-
-        private boolean isIndividualSensorPrivacyEnabledLocked(int userId, int sensor) {
-            SparseArray<SensorState> states = mIndividualEnabled.get(userId);
-            if (states == null) {
-                return false;
-            }
-            SensorState state = states.get(sensor);
-            if (state == null) {
-                return false;
-            }
-            return state.mEnabled;
-        }
-
-        /**
-         * Returns the state of sensor privacy from persistent storage.
-         */
-        private boolean readPersistedSensorPrivacyStateLocked() {
-            // if the file does not exist then sensor privacy has not yet been enabled on
-            // the device.
-
-            SparseArray<Object> map = new SparseArray<>();
-            int version = -1;
-
-            if (mAtomicFile.exists()) {
-                try (FileInputStream inputStream = mAtomicFile.openRead()) {
-                    TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
-                    XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
-                    final int persistenceVersion = parser.getAttributeInt(null,
-                            XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
-
-                    // Use inline string literals for xml tags/attrs when parsing old versions since
-                    // these should never be changed even with refactorings.
-                    if (persistenceVersion == 0) {
-                        boolean enabled = parser.getAttributeBoolean(null, "enabled", false);
-                        SparseArray<SensorState> individualEnabled = new SparseArray<>();
-                        version = 0;
-
-                        XmlUtils.nextElement(parser);
-                        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                            String tagName = parser.getName();
-                            if ("individual-sensor-privacy".equals(tagName)) {
-                                int sensor = XmlUtils.readIntAttribute(parser, "sensor");
-                                boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
-                                        "enabled");
-                                individualEnabled.put(sensor, new SensorState(indEnabled));
-                                XmlUtils.skipCurrentTag(parser);
-                            } else {
-                                XmlUtils.nextElement(parser);
-                            }
-                        }
-                        map.put(VER0_ENABLED, enabled);
-                        map.put(VER0_INDIVIDUAL_ENABLED, individualEnabled);
-                    } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
-                        SparseBooleanArray enabled = new SparseBooleanArray();
-                        SparseArray<SparseArray<SensorState>> individualEnabled =
-                                new SparseArray<>();
-                        version = parser.getAttributeInt(null,
-                                XML_ATTRIBUTE_VERSION, 1);
-
-                        int currentUserId = -1;
-                        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                            XmlUtils.nextElement(parser);
-                            String tagName = parser.getName();
-                            if (XML_TAG_USER.equals(tagName)) {
-                                currentUserId = parser.getAttributeInt(null, XML_ATTRIBUTE_ID);
-                                boolean isEnabled = parser.getAttributeBoolean(null,
-                                        XML_ATTRIBUTE_ENABLED);
-                                if (enabled.indexOfKey(currentUserId) >= 0) {
-                                    Log.e(TAG, "User listed multiple times in file.",
-                                            new RuntimeException());
-                                    mAtomicFile.delete();
-                                    version = -1;
-                                    break;
-                                }
-
-                                if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
-                                    // User may no longer exist, skip this user
-                                    currentUserId = -1;
-                                    continue;
-                                }
-
-                                enabled.put(currentUserId, isEnabled);
-                            }
-                            if (XML_TAG_INDIVIDUAL_SENSOR_PRIVACY.equals(tagName)) {
-                                if (mUserManagerInternal.getUserInfo(currentUserId) == null) {
-                                    // User may no longer exist or isn't set
-                                    continue;
-                                }
-                                int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
-                                boolean isEnabled = parser.getAttributeBoolean(null,
-                                        XML_ATTRIBUTE_ENABLED);
-                                long lastChange = parser
-                                        .getAttributeLong(null, XML_ATTRIBUTE_LAST_CHANGE, -1);
-                                SparseArray<SensorState> userIndividualEnabled =
-                                        individualEnabled.get(currentUserId, new SparseArray<>());
-
-                                userIndividualEnabled
-                                        .put(sensor, new SensorState(isEnabled, lastChange));
-                                individualEnabled.put(currentUserId, userIndividualEnabled);
-                            }
-                        }
-
-                        map.put(VER1_ENABLED, enabled);
-                        map.put(VER1_INDIVIDUAL_ENABLED, individualEnabled);
-                    } else {
-                        Log.e(TAG, "Unknown persistence version: " + persistenceVersion
-                                        + ". Deleting.",
-                                new RuntimeException());
-                        mAtomicFile.delete();
-                        version = -1;
-                    }
-
-                } catch (IOException | XmlPullParserException e) {
-                    Log.e(TAG, "Caught an exception reading the state from storage: ", e);
-                    // Delete the file to prevent the same error on subsequent calls and assume
-                    // sensor privacy is not enabled.
-                    mAtomicFile.delete();
-                    version = -1;
-                }
-            }
-
-            try {
-                return upgradeAndInit(version, map);
-            } catch (Exception e) {
-                Log.wtf(TAG, "Failed to upgrade and set sensor privacy state,"
-                        + " resetting to default.", e);
-                mEnabled = new SparseBooleanArray();
-                mIndividualEnabled = new SparseArray<>();
-                return true;
-            }
-        }
-
-        private boolean upgradeAndInit(int version, SparseArray map) {
-            if (version == -1) {
-                // New file, default state for current version goes here.
-                mEnabled = new SparseBooleanArray();
-                mIndividualEnabled = new SparseArray<>();
-                forAllUsers(userId -> mEnabled.put(userId, false));
-                forAllUsers(userId -> mIndividualEnabled.put(userId, new SparseArray<>()));
-                return true;
-            }
-            boolean upgraded = false;
-            final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
-            if (version == 0) {
-                final boolean enabled = (boolean) map.get(VER0_ENABLED);
-                final SparseArray<SensorState> individualEnabled =
-                        (SparseArray<SensorState>) map.get(VER0_INDIVIDUAL_ENABLED);
-
-                final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
-                final SparseArray<SparseArray<SensorState>> perUserIndividualEnabled =
-                        new SparseArray<>();
-
-                // Copy global state to each user
-                for (int i = 0; i < users.length; i++) {
-                    int user = users[i];
-                    perUserEnabled.put(user, enabled);
-                    SparseArray<SensorState> userIndividualSensorEnabled = new SparseArray<>();
-                    perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
-                    for (int j = 0; j < individualEnabled.size(); j++) {
-                        final int sensor = individualEnabled.keyAt(j);
-                        final SensorState isSensorEnabled = individualEnabled.valueAt(j);
-                        userIndividualSensorEnabled.put(sensor, isSensorEnabled);
-                    }
-                }
-
-                map.clear();
-                map.put(VER1_ENABLED, perUserEnabled);
-                map.put(VER1_INDIVIDUAL_ENABLED, perUserIndividualEnabled);
-
-                version = 1;
-                upgraded = true;
-            }
-            if (version == CURRENT_VERSION) {
-                mEnabled = (SparseBooleanArray) map.get(VER1_ENABLED);
-                mIndividualEnabled =
-                        (SparseArray<SparseArray<SensorState>>) map.get(VER1_INDIVIDUAL_ENABLED);
-            }
-            return upgraded;
-        }
-
-        /**
-         * Persists the state of sensor privacy.
-         */
-        private void persistSensorPrivacyState() {
-            synchronized (mLock) {
-                persistSensorPrivacyStateLocked();
-            }
-        }
-
-        private void persistSensorPrivacyStateLocked() {
-            FileOutputStream outputStream = null;
-            try {
-                outputStream = mAtomicFile.startWrite();
-                TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
-                serializer.startDocument(null, true);
-                serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
-                serializer.attributeInt(
-                        null, XML_ATTRIBUTE_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
-                serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
-                forAllUsers(userId -> {
-                    serializer.startTag(null, XML_TAG_USER);
-                    serializer.attributeInt(null, XML_ATTRIBUTE_ID, userId);
-                    serializer.attributeBoolean(
-                            null, XML_ATTRIBUTE_ENABLED, isSensorPrivacyEnabled(userId));
-
-                    SparseArray<SensorState> individualEnabled =
-                            mIndividualEnabled.get(userId, new SparseArray<>());
-                    int numIndividual = individualEnabled.size();
-                    for (int i = 0; i < numIndividual; i++) {
-                        serializer.startTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
-                        int sensor = individualEnabled.keyAt(i);
-                        SensorState sensorState = individualEnabled.valueAt(i);
-                        boolean enabled = sensorState.mEnabled;
-                        long lastChange = sensorState.mLastChange;
-                        serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR, sensor);
-                        serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
-                        serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CHANGE, lastChange);
-                        serializer.endTag(null, XML_TAG_INDIVIDUAL_SENSOR_PRIVACY);
-                    }
-                    serializer.endTag(null, XML_TAG_USER);
-
-                });
-                serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
-                serializer.endDocument();
-                mAtomicFile.finishWrite(outputStream);
-            } catch (IOException e) {
-                Log.e(TAG, "Caught an exception persisting the sensor privacy state: ", e);
-                mAtomicFile.failWrite(outputStream);
-            }
+            return mSensorPrivacyStateController.getState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+                    userId, sensor).isEnabled();
         }
 
         @Override
@@ -1285,23 +980,23 @@
         }
 
         private void userSwitching(int from, int to) {
-            boolean micState;
-            boolean camState;
-            boolean prevMicState;
-            boolean prevCamState;
-            synchronized (mLock) {
-                prevMicState = isIndividualSensorPrivacyEnabledLocked(from, MICROPHONE);
-                prevCamState = isIndividualSensorPrivacyEnabledLocked(from, CAMERA);
-                micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE);
-                camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA);
+            final boolean[] micState = new boolean[1];
+            final boolean[] camState = new boolean[1];
+            final boolean[] prevMicState = new boolean[1];
+            final boolean[] prevCamState = new boolean[1];
+            mSensorPrivacyStateController.atomic(() -> {
+                prevMicState[0] = isIndividualSensorPrivacyEnabled(from, MICROPHONE);
+                prevCamState[0] = isIndividualSensorPrivacyEnabled(from, CAMERA);
+                micState[0] = isIndividualSensorPrivacyEnabled(to, MICROPHONE);
+                camState[0] = isIndividualSensorPrivacyEnabled(to, CAMERA);
+            });
+            if (from == USER_NULL || prevMicState[0] != micState[0]) {
+                mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState[0]);
+                setGlobalRestriction(MICROPHONE, micState[0]);
             }
-            if (from == USER_NULL || prevMicState != micState) {
-                mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState);
-                setGlobalRestriction(MICROPHONE, micState);
-            }
-            if (from == USER_NULL || prevCamState != camState) {
-                mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState);
-                setGlobalRestriction(CAMERA, camState);
+            if (from == USER_NULL || prevCamState[0] != camState[0]) {
+                mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState[0]);
+                setGlobalRestriction(CAMERA, camState[0]);
             }
         }
 
@@ -1395,12 +1090,14 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 if (dumpAsProto) {
-                    dump(new DualDumpOutputStream(new ProtoOutputStream(fd)));
+                    mSensorPrivacyStateController.dump(
+                            new DualDumpOutputStream(new ProtoOutputStream(fd)));
                 } else {
                     pw.println("SENSOR PRIVACY MANAGER STATE (dumpsys "
                             + Context.SENSOR_PRIVACY_SERVICE + ")");
 
-                    dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")));
+                    mSensorPrivacyStateController.dump(
+                            new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")));
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1408,45 +1105,6 @@
         }
 
         /**
-         * Dump state to {@link DualDumpOutputStream}.
-         *
-         * @param dumpStream The destination to dump to
-         */
-        private void dump(@NonNull DualDumpOutputStream dumpStream) {
-            synchronized (mLock) {
-
-                forAllUsers(userId -> {
-                    long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
-                    dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
-                    dumpStream.write("is_enabled", SensorPrivacyUserProto.IS_ENABLED,
-                            mEnabled.get(userId, false));
-
-                    SparseArray<SensorState> individualEnabled = mIndividualEnabled.get(userId);
-                    if (individualEnabled != null) {
-                        int numIndividualEnabled = individualEnabled.size();
-                        for (int i = 0; i < numIndividualEnabled; i++) {
-                            long individualToken = dumpStream.start("individual_enabled_sensor",
-                                    SensorPrivacyUserProto.INDIVIDUAL_ENABLED_SENSOR);
-
-                            dumpStream.write("sensor",
-                                    SensorPrivacyIndividualEnabledSensorProto.SENSOR,
-                                    individualEnabled.keyAt(i));
-                            dumpStream.write("is_enabled",
-                                    SensorPrivacyIndividualEnabledSensorProto.IS_ENABLED,
-                                    individualEnabled.valueAt(i).mEnabled);
-                            // TODO dump last change
-
-                            dumpStream.end(individualToken);
-                        }
-                    }
-                    dumpStream.end(userToken);
-                });
-            }
-
-            dumpStream.flush();
-        }
-
-        /**
          * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}.
          *
          * @param sensor The name to convert
@@ -1504,25 +1162,6 @@
                             setIndividualSensorPrivacy(userId, SHELL, sensor, false);
                         }
                         break;
-                        case "reset": {
-                            int sensor = sensorStrToId(getNextArgRequired());
-                            if (sensor == UNKNOWN) {
-                                pw.println("Invalid sensor");
-                                return -1;
-                            }
-
-                            enforceManageSensorPrivacyPermission();
-
-                            synchronized (mLock) {
-                                SparseArray<SensorState> individualEnabled =
-                                        mIndividualEnabled.get(userId);
-                                if (individualEnabled != null) {
-                                    individualEnabled.delete(sensor);
-                                }
-                                persistSensorPrivacyState();
-                            }
-                        }
-                        break;
                         default:
                             return handleDefaultCommands(cmd);
                     }
@@ -1545,9 +1184,6 @@
                     pw.println("  disable USER_ID SENSOR");
                     pw.println("    Disable privacy for a certain sensor.");
                     pw.println("");
-                    pw.println("  reset USER_ID SENSOR");
-                    pw.println("    Reset privacy state for a certain sensor.");
-                    pw.println("");
                 }
             }).exec(this, in, out, err, args, callback, resultReceiver);
         }
@@ -1581,22 +1217,6 @@
             mContext = context;
         }
 
-        public void onSensorPrivacyChanged(boolean enabled) {
-            sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
-                    this, enabled));
-            sendMessage(
-                    PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
-                            mSensorPrivacyServiceImpl));
-        }
-
-        public void onSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
-            sendMessage(PooledLambda.obtainMessage(SensorPrivacyHandler::handleSensorPrivacyChanged,
-                    this, userId, sensor, enabled));
-            sendMessage(
-                    PooledLambda.obtainMessage(SensorPrivacyServiceImpl::persistSensorPrivacyState,
-                            mSensorPrivacyServiceImpl));
-        }
-
         public void onUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) {
             sendMessage(PooledLambda.obtainMessage(
                     SensorPrivacyHandler::handleUserGlobalSensorPrivacyChanged,
@@ -1692,6 +1312,7 @@
         }
 
         public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+            // TODO handle hardware
             mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
             SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
                     mIndividualSensorListeners.get(userId);
@@ -1972,11 +1593,7 @@
         }
     }
 
-    private static long getCurrentTimeMillis() {
-        try {
-            return SystemClock.currentNetworkTimeMillis();
-        } catch (Exception e) {
-            return System.currentTimeMillis();
-        }
+    static long getCurrentTimeMillis() {
+        return SystemClock.elapsedRealtime();
     }
 }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
new file mode 100644
index 0000000..9694958
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.os.Handler;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+abstract class SensorPrivacyStateController {
+
+    private static SensorPrivacyStateController sInstance;
+
+    AllSensorStateController mAllSensorStateController = AllSensorStateController.getInstance();
+
+    private final Object mLock = new Object();
+
+    static SensorPrivacyStateController getInstance() {
+        if (sInstance == null) {
+            sInstance = SensorPrivacyStateControllerImpl.getInstance();
+        }
+
+        return sInstance;
+    }
+
+    SensorState getState(int toggleType, int userId, int sensor) {
+        synchronized (mLock) {
+            return getStateLocked(toggleType, userId, sensor);
+        }
+    }
+
+    void setState(int toggleType, int userId, int sensor, boolean enabled, Handler callbackHandler,
+            SetStateResultCallback callback) {
+        synchronized (mLock) {
+            setStateLocked(toggleType, userId, sensor, enabled, callbackHandler, callback);
+        }
+    }
+
+    void setSensorPrivacyListener(Handler handler,
+            SensorPrivacyListener listener) {
+        synchronized (mLock) {
+            setSensorPrivacyListenerLocked(handler, listener);
+        }
+    }
+
+    // Following calls are for the developer settings sensor mute feature
+    boolean getAllSensorState() {
+        synchronized (mLock) {
+            return mAllSensorStateController.getAllSensorStateLocked();
+        }
+    }
+
+    void setAllSensorState(boolean enable) {
+        synchronized (mLock) {
+            mAllSensorStateController.setAllSensorStateLocked(enable);
+        }
+    }
+
+    void setAllSensorPrivacyListener(Handler handler, AllSensorPrivacyListener listener) {
+        synchronized (mLock) {
+            mAllSensorStateController.setAllSensorPrivacyListenerLocked(handler, listener);
+        }
+    }
+
+    void persistAll() {
+        synchronized (mLock) {
+            mAllSensorStateController.schedulePersistLocked();
+            schedulePersistLocked();
+        }
+    }
+
+    void forEachState(SensorPrivacyStateConsumer consumer) {
+        synchronized (mLock) {
+            forEachStateLocked(consumer);
+        }
+    }
+
+    void dump(DualDumpOutputStream dumpStream) {
+        synchronized (mLock) {
+            mAllSensorStateController.dumpLocked(dumpStream);
+            dumpLocked(dumpStream);
+        }
+        dumpStream.flush();
+    }
+
+    public void atomic(Runnable r) {
+        synchronized (mLock) {
+            r.run();
+        }
+    }
+
+    interface SensorPrivacyListener {
+        void onSensorPrivacyChanged(int toggleType, int userId, int sensor, SensorState state);
+    }
+
+    interface SensorPrivacyStateConsumer {
+        void accept(int toggleType, int userId, int sensor, SensorState state);
+    }
+
+    interface SetStateResultCallback {
+        void callback(boolean changed);
+    }
+
+    interface AllSensorPrivacyListener {
+        void onAllSensorPrivacyChanged(boolean enabled);
+    }
+
+    @GuardedBy("mLock")
+    abstract SensorState getStateLocked(int toggleType, int userId, int sensor);
+
+    @GuardedBy("mLock")
+    abstract void setStateLocked(int toggleType, int userId, int sensor, boolean enabled,
+            Handler callbackHandler, SetStateResultCallback callback);
+
+    @GuardedBy("mLock")
+    abstract void setSensorPrivacyListenerLocked(Handler handler,
+            SensorPrivacyListener listener);
+
+    @GuardedBy("mLock")
+    abstract void schedulePersistLocked();
+
+    @GuardedBy("mLock")
+    abstract void forEachStateLocked(SensorPrivacyStateConsumer consumer);
+
+    @GuardedBy("mLock")
+    abstract void dumpLocked(DualDumpOutputStream dumpStream);
+
+    static void sendSetStateCallback(Handler callbackHandler,
+            SetStateResultCallback callback, boolean success) {
+        callbackHandler.sendMessage(PooledLambda.obtainMessage(SetStateResultCallback::callback,
+                callback, success));
+    }
+
+    /**
+     * Used for unit testing
+     */
+    void resetForTesting() {
+        mAllSensorStateController.resetForTesting();
+        resetForTestingImpl();
+        sInstance = null;
+    }
+    abstract void resetForTestingImpl();
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
new file mode 100644
index 0000000..d1ea8e9
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.Objects;
+
+class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController {
+
+    private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy_impl.xml";
+
+    private static SensorPrivacyStateControllerImpl sInstance;
+
+    private PersistedState mPersistedState;
+
+    private SensorPrivacyListener mListener;
+    private Handler mListenerHandler;
+
+    static SensorPrivacyStateController getInstance() {
+        if (sInstance == null) {
+            sInstance = new SensorPrivacyStateControllerImpl();
+        }
+        return sInstance;
+    }
+
+    private SensorPrivacyStateControllerImpl() {
+        mPersistedState = PersistedState.fromFile(SENSOR_PRIVACY_XML_FILE);
+        persistAll();
+    }
+
+    @Override
+    SensorState getStateLocked(int toggleType, int userId, int sensor) {
+        if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
+            // Device doesn't support hardware state
+            return getDefaultSensorState();
+        }
+        SensorState sensorState = mPersistedState.getState(toggleType, userId, sensor);
+        if (sensorState != null) {
+            return new SensorState(sensorState);
+        }
+        return getDefaultSensorState();
+    }
+
+    private static SensorState getDefaultSensorState() {
+        return new SensorState(false);
+    }
+
+    @Override
+    void setStateLocked(int toggleType, int userId, int sensor, boolean enabled,
+            Handler callbackHandler, SetStateResultCallback callback) {
+        if (toggleType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+            // Implementation only supports software switch
+            callbackHandler.sendMessage(PooledLambda.obtainMessage(
+                    SetStateResultCallback::callback, callback, false));
+            return;
+        }
+        // Changing the SensorState's mEnabled updates the timestamp of its last change.
+        // A nonexistent state -> unmuted should not set the timestamp.
+        SensorState lastState = mPersistedState.getState(toggleType, userId, sensor);
+        if (lastState == null) {
+            if (!enabled) {
+                sendSetStateCallback(callbackHandler, callback, false);
+                return;
+            } else if (enabled) {
+                SensorState sensorState = new SensorState(true);
+                mPersistedState.setState(toggleType, userId, sensor, sensorState);
+                notifyStateChangeLocked(toggleType, userId, sensor, sensorState);
+                sendSetStateCallback(callbackHandler, callback, true);
+                return;
+            }
+        }
+        if (lastState.setEnabled(enabled)) {
+            notifyStateChangeLocked(toggleType, userId, sensor, lastState);
+            sendSetStateCallback(callbackHandler, callback, true);
+            return;
+        }
+        sendSetStateCallback(callbackHandler, callback, false);
+    }
+
+    private void notifyStateChangeLocked(int toggleType, int userId, int sensor,
+            SensorState sensorState) {
+        if (mListenerHandler != null && mListener != null) {
+            mListenerHandler.sendMessage(PooledLambda.obtainMessage(
+                    SensorPrivacyListener::onSensorPrivacyChanged, mListener,
+                    toggleType, userId, sensor, new SensorState(sensorState)));
+        }
+        schedulePersistLocked();
+    }
+
+    @Override
+    void setSensorPrivacyListenerLocked(Handler handler, SensorPrivacyListener listener) {
+        Objects.requireNonNull(handler);
+        Objects.requireNonNull(listener);
+        if (mListener != null) {
+            throw new IllegalStateException("Listener is already set");
+        }
+        mListener = listener;
+        mListenerHandler = handler;
+    }
+
+    @Override
+    void schedulePersistLocked() {
+        mPersistedState.schedulePersist();
+    }
+
+    @Override
+    void forEachStateLocked(SensorPrivacyStateConsumer consumer) {
+        mPersistedState.forEachKnownState(consumer::accept);
+    }
+
+    @Override
+    void resetForTestingImpl() {
+        mPersistedState.resetForTesting();
+        mListener = null;
+        mListenerHandler = null;
+        sInstance = null;
+    }
+
+    @Override
+    void dumpLocked(DualDumpOutputStream dumpStream) {
+        mPersistedState.dump(dumpStream);
+    }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorState.java b/services/core/java/com/android/server/sensorprivacy/SensorState.java
new file mode 100644
index 0000000..b92e2c8
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/SensorState.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensorprivacy;
+
+import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED;
+import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED;
+
+import static com.android.server.sensorprivacy.SensorPrivacyService.getCurrentTimeMillis;
+
+class SensorState {
+
+    private int mStateType;
+    private long mLastChange;
+
+    SensorState(int stateType) {
+        mStateType = stateType;
+        mLastChange = getCurrentTimeMillis();
+    }
+
+    SensorState(int stateType, long lastChange) {
+        mStateType = stateType;
+        mLastChange = Math.min(getCurrentTimeMillis(), lastChange);
+    }
+
+    SensorState(SensorState sensorState) {
+        mStateType = sensorState.getState();
+        mLastChange = sensorState.getLastChange();
+    }
+
+    boolean setState(int stateType) {
+        if (mStateType != stateType) {
+            mStateType = stateType;
+            mLastChange = getCurrentTimeMillis();
+            return true;
+        }
+        return false;
+    }
+
+    int getState() {
+        return mStateType;
+    }
+
+    long getLastChange() {
+        return mLastChange;
+    }
+
+    // Below are some convenience members for when dealing with enabled/disabled
+
+    private static int enabledToState(boolean enabled) {
+        return enabled ? ENABLED : DISABLED;
+    }
+
+    SensorState(boolean enabled) {
+        this(enabledToState(enabled));
+    }
+
+    boolean setEnabled(boolean enabled) {
+        return setState(enabledToState(enabled));
+    }
+
+    boolean isEnabled() {
+        return getState() == ENABLED;
+    }
+
+}
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index ff2f08b..27c0bee 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -15,34 +15,56 @@
  */
 package com.android.server.tracing;
 
+import android.Manifest;
+import android.annotation.NonNull;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.os.Binder;
+import android.os.IMessenger;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import android.os.UserHandle;
+import android.service.tracing.TraceReportService;
 import android.tracing.ITracingServiceProxy;
+import android.tracing.TraceReportParams;
 import android.util.Log;
+import android.util.LruCache;
+import android.util.Slog;
 
+import com.android.internal.infra.ServiceConnector;
 import com.android.server.SystemService;
 
+import java.io.IOException;
+
 /**
  * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
- * system tracing app Traceur.
+ * other components (e.g. system tracing app Traceur, trace reporting apps).
  *
  * Access to this service is restricted via SELinux. Normal apps do not have access.
  *
  * @hide
  */
 public class TracingServiceProxy extends SystemService {
-    private static final String TAG = "TracingServiceProxy";
-
     public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
-
+    private static final String TAG = "TracingServiceProxy";
     private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
     private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
 
+    private static final int MAX_CACHED_REPORTER_SERVICES = 8;
+
+    // The maximum size of the trace allowed if the option |usePipeForTesting| is set.
+    // Note: this size MUST be smaller than the buffer size of the pipe (i.e. what you can
+    // write to the pipe without blocking) to avoid system_server blocking on this.
+    // (on Linux, the minimum value is 4K i.e. 1 minimally sized page).
+    private static final int MAX_FILE_SIZE_BYTES_TO_PIPE = 1024;
+
     // Keep this in sync with the definitions in TraceService
     private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
             "com.android.traceur.NOTIFY_SESSION_STOPPED";
@@ -51,16 +73,22 @@
 
     private final Context mContext;
     private final PackageManager mPackageManager;
+    private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
 
     private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
         /**
-          * Notifies system tracing app that a tracing session has ended. If a session is repurposed
-          * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
-          * there is no buffer available to dump.
-          */
+         * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+         * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+         * there is no buffer available to dump.
+         */
         @Override
         public void notifyTraceSessionEnded(boolean sessionStolen) {
-            notifyTraceur(sessionStolen);
+            TracingServiceProxy.this.notifyTraceur(sessionStolen);
+        }
+
+        @Override
+        public void reportTrace(@NonNull TraceReportParams params) {
+            TracingServiceProxy.this.reportTrace(params);
         }
     };
 
@@ -68,6 +96,7 @@
         super(context);
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mCachedReporterServices = new LruCache<>(MAX_CACHED_REPORTER_SERVICES);
     }
 
     @Override
@@ -103,4 +132,119 @@
             Log.e(TAG, "Failed to locate Traceur", e);
         }
     }
+
+    private void reportTrace(@NonNull TraceReportParams params) {
+        // We don't need to do any permission checks on the caller because access
+        // to this service is guarded by SELinux.
+        ComponentName component = new ComponentName(params.reporterPackageName,
+                params.reporterClassName);
+        if (!hasBindServicePermission(component)) {
+            return;
+        }
+        boolean hasDumpPermission = hasPermission(component, Manifest.permission.DUMP);
+        boolean hasUsageStatsPermission = hasPermission(component,
+                Manifest.permission.PACKAGE_USAGE_STATS);
+        if (!hasDumpPermission || !hasUsageStatsPermission) {
+            return;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            reportTrace(getOrCreateReporterService(component), params);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void reportTrace(
+            @NonNull ServiceConnector<IMessenger> reporterService,
+            @NonNull TraceReportParams params) {
+        reporterService.post(messenger -> {
+            if (params.usePipeForTesting) {
+                ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+                try (AutoCloseInputStream i = new AutoCloseInputStream(params.fd)) {
+                    try (AutoCloseOutputStream o = new AutoCloseOutputStream(pipe[1])) {
+                        byte[] array = i.readNBytes(MAX_FILE_SIZE_BYTES_TO_PIPE);
+                        if (array.length == MAX_FILE_SIZE_BYTES_TO_PIPE) {
+                            throw new IllegalArgumentException(
+                                    "Trace file too large when |usePipeForTesting| is set.");
+                        }
+                        o.write(array);
+                    }
+                }
+                params.fd = pipe[0];
+            }
+
+            Message message = Message.obtain();
+            message.what = TraceReportService.MSG_REPORT_TRACE;
+            message.obj = params;
+            messenger.send(message);
+        }).whenComplete((res, err) -> {
+            if (err != null) {
+                Slog.e(TAG, "Failed to report trace", err);
+            }
+            try {
+                params.fd.close();
+            } catch (IOException ignored) {
+            }
+        });
+    }
+
+    private ServiceConnector<IMessenger> getOrCreateReporterService(
+            @NonNull ComponentName component) {
+        ServiceConnector<IMessenger> connector = mCachedReporterServices.get(component);
+        if (connector == null) {
+            Intent intent = new Intent();
+            intent.setComponent(component);
+            connector = new ServiceConnector.Impl<IMessenger>(
+                    mContext, intent,
+                    Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY,
+                    mContext.getUser().getIdentifier(), IMessenger.Stub::asInterface) {
+                private static final long DISCONNECT_TIMEOUT_MS = 15_000;
+                private static final long REQUEST_TIMEOUT_MS = 10_000;
+
+                @Override
+                protected long getAutoDisconnectTimeoutMs() {
+                    return DISCONNECT_TIMEOUT_MS;
+                }
+
+                @Override
+                protected long getRequestTimeoutMs() {
+                    return REQUEST_TIMEOUT_MS;
+                }
+            };
+            mCachedReporterServices.put(intent.getComponent(), connector);
+        }
+        return connector;
+    }
+
+    private boolean hasPermission(@NonNull ComponentName componentName,
+            @NonNull String permission) throws SecurityException {
+        if (mPackageManager.checkPermission(permission, componentName.getPackageName())
+                != PackageManager.PERMISSION_GRANTED) {
+            Slog.e(TAG,
+                    "Trace reporting service " + componentName.toShortString() + " does not have "
+                            + permission + " permission");
+            return false;
+        }
+        return true;
+    }
+
+    private boolean hasBindServicePermission(@NonNull ComponentName componentName) {
+        ServiceInfo info;
+        try {
+            info = mPackageManager.getServiceInfo(componentName, 0);
+        } catch (NameNotFoundException ex) {
+            Slog.e(TAG,
+                    "Trace reporting service " + componentName.toShortString() + " does not exist");
+            return false;
+        }
+        if (!Manifest.permission.BIND_TRACE_REPORT_SERVICE.equals(info.permission)) {
+            Slog.e(TAG,
+                    "Trace reporting service " + componentName.toShortString()
+                            + " does not request " + Manifest.permission.BIND_TRACE_REPORT_SERVICE
+                            + " permission; instead requests " + info.permission);
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7164c6c..ff96aeb 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -43,6 +43,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
 
+import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -1081,7 +1082,7 @@
 
     @Override
     public void overridePendingTransition(IBinder token, String packageName,
-            int enterAnim, int exitAnim) {
+            int enterAnim, int exitAnim, @ColorInt int backgroundColor) {
         final long origId = Binder.clearCallingIdentity();
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
@@ -1091,7 +1092,7 @@
                         r.mOverrideTaskTransition);
                 r.mTransitionController.setOverrideAnimation(
                         TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
-                                enterAnim, exitAnim, r.mOverrideTaskTransition),
+                                enterAnim, exitAnim, backgroundColor, r.mOverrideTaskTransition),
                         null /* startCallback */, null /* finishCallback */);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c0eee61..3908874 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4469,6 +4469,7 @@
                         pendingOptions.getOverrideTaskTransition());
                 options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
                         pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+                        pendingOptions.getCustomBackgroundColor(),
                         pendingOptions.getOverrideTaskTransition());
                 startCallback = pendingOptions.getAnimationStartedListener();
                 finishCallback = pendingOptions.getAnimationFinishedListener();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5d879ce..3d7dead 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -906,8 +906,7 @@
                         proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
                         results, newIntents, r.takeOptions(), isTransitionForward,
                         proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
-                        r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
-                        r.getLaunchedFromBubble()));
+                        r.shareableActivityToken, r.getLaunchedFromBubble()));
 
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a1c823e..4009220 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -170,7 +170,6 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
-import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -1684,9 +1683,6 @@
             // adjustments of previous rotated activity should be cleared earlier. Otherwise if
             // the current top is in the same process, it may get the rotated state. The transform
             // will be cleared later with transition callback to ensure smooth animation.
-            if (hasTopFixedRotationLaunchingApp()) {
-                mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */);
-            }
             return false;
         }
         if (!r.getDisplayArea().matchParentBounds()) {
@@ -2191,8 +2187,8 @@
         outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
 
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,
-                dh);
+        outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw, dh);
+        outConfig.windowConfiguration.setDisplayRotation(rotation);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 0e2d847..8db4306 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -41,6 +41,8 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
     /* maps input token to an embedded window */
     private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
+    private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
+        new ArrayMap<>();
     private final Object mGlobalLock;
     private final ActivityTaskManagerService mAtmService;
 
@@ -59,10 +61,13 @@
     void add(IBinder inputToken, EmbeddedWindow window) {
         try {
             mWindows.put(inputToken, window);
+            final IBinder focusToken = window.getFocusGrantToken();
+            mWindowsByFocusToken.put(focusToken, window);
             updateProcessController(window);
             window.mClient.asBinder().linkToDeath(()-> {
                 synchronized (mGlobalLock) {
                     mWindows.remove(inputToken);
+                    mWindowsByFocusToken.remove(focusToken);
                 }
             }, 0);
         } catch (RemoteException e) {
@@ -98,8 +103,8 @@
         return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
     }
 
-    void setIsOverlay(IBinder inputToken) {
-        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+    void setIsOverlay(IBinder focusGrantToken) {
+        EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
         if (embeddedWindow != null) {
             embeddedWindow.setIsOverlay();
         }
@@ -107,8 +112,10 @@
 
     void remove(IWindow client) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
-            if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
+            EmbeddedWindow ew = mWindows.valueAt(i);
+            if (ew.mClient.asBinder() == client.asBinder()) {
                 mWindows.removeAt(i).onRemoved();
+                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
                 return;
             }
         }
@@ -116,8 +123,10 @@
 
     void onWindowRemoved(WindowState host) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
-            if (mWindows.valueAt(i).mHostWindowState == host) {
+            EmbeddedWindow ew = mWindows.valueAt(i);
+            if (ew.mHostWindowState == host) {
                 mWindows.removeAt(i).onRemoved();
+                mWindowsByFocusToken.remove(ew.getFocusGrantToken());
             }
         }
     }
@@ -126,6 +135,10 @@
         return mWindows.get(inputToken);
     }
 
+    EmbeddedWindow getByFocusToken(IBinder focusGrantToken) {
+        return mWindowsByFocusToken.get(focusGrantToken);
+    }
+
     void onActivityRemoved(ActivityRecord activityRecord) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             final EmbeddedWindow window = mWindows.valueAt(i);
@@ -157,6 +170,8 @@
         // and this variable is mostly used for tracking that.
         boolean mIsOverlay = false;
 
+        private IBinder mFocusGrantToken;
+
         /**
          * @param session  calling session to check ownership of the window
          * @param clientToken client token used to clean up the map if the embedding process dies
@@ -171,7 +186,7 @@
          */
         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
-                       int displayId) {
+                       int displayId, IBinder focusGrantToken) {
             mSession = session;
             mWmService = service;
             mClient = clientToken;
@@ -182,6 +197,7 @@
             mOwnerPid = ownerPid;
             mWindowType = windowType;
             mDisplayId = displayId;
+            mFocusGrantToken = focusGrantToken;
         }
 
         @Override
@@ -242,6 +258,17 @@
             return mIsOverlay;
         }
 
+        IBinder getFocusGrantToken() {
+            return mFocusGrantToken;
+        }
+
+        IBinder getInputChannelToken() {
+            if (mInputChannel != null) {
+                return mInputChannel.getToken();
+            }
+            return null;
+        }
+
         /**
          * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
          * so we need to participate inside handlePointerDownOutsideFocus logic
@@ -255,7 +282,7 @@
 
         private void handleTap(boolean grantFocus) {
             if (mInputChannel != null) {
-                mWmService.grantEmbeddedWindowFocus(mSession, mInputChannel.getToken(), grantFocus);
+                mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 9326a2e..10776ab 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -219,13 +219,23 @@
     /**
      * @see InsetsStateController#getInsetsForWindow
      */
-    InsetsState getInsetsForWindow(WindowState target) {
+    InsetsState getInsetsForWindow(WindowState target, boolean includesTransient) {
         final InsetsState originalState = mStateController.getInsetsForWindow(target);
-        InsetsState state = adjustVisibilityForTransientTypes(originalState);
+        InsetsState state;
+        if (!includesTransient) {
+            state = adjustVisibilityForTransientTypes(originalState);
+        } else {
+            state = originalState;
+        }
         state = adjustVisibilityForIme(target, state, state == originalState);
         return adjustInsetsForRoundedCorners(target, state, state == originalState);
     }
 
+    InsetsState getInsetsForWindow(WindowState target) {
+        return getInsetsForWindow(target, false);
+    }
+
+
     /**
      * @see InsetsStateController#getInsetsForWindowMetrics
      */
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/OverlayHost.java
index 724e124..90f5b09 100644
--- a/services/core/java/com/android/server/wm/OverlayHost.java
+++ b/services/core/java/com/android/server/wm/OverlayHost.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 
@@ -121,6 +123,16 @@
         }
     }
 
+    void dispatchInsetsChanged(InsetsState s, Rect insetFrame) {
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+            SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+            try {
+                l.getRemoteInterface().onInsetsChanged(s, insetFrame);
+            } catch (Exception e) {
+            }
+        }
+    }
+
     void release() {
         dispatchDetachedFromWindow();
         mOverlays.clear();
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index a049d65..d4a7a5d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -380,8 +380,11 @@
         final ComponentName cn = ComponentName.unflattenFromString(rawRecentsComponent);
         if (cn != null) {
             try {
-                final ApplicationInfo appInfo = AppGlobals.getPackageManager()
-                        .getApplicationInfo(cn.getPackageName(), 0, mService.mContext.getUserId());
+                final ApplicationInfo appInfo = AppGlobals.getPackageManager().getApplicationInfo(
+                        cn.getPackageName(),
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                | PackageManager.MATCH_DISABLED_COMPONENTS,
+                        mService.mContext.getUserId());
                 if (appInfo != null) {
                     mRecentsUid = appInfo.uid;
                     mRecentsComponent = cn;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 9b94f44..98acc46 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -813,7 +813,7 @@
     @Override
     public void grantInputChannel(int displayId, SurfaceControl surface,
             IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
-            InputChannel outInputChannel) {
+            IBinder focusGrantToken, InputChannel outInputChannel) {
         if (hostInputToken == null && !mCanAddInternalSystemWindow) {
             // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
             // embedded windows without providing a host window input token
@@ -829,7 +829,7 @@
         try {
             mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
                     flags, mCanAddInternalSystemWindow ? privateFlags : 0,
-                    mCanAddInternalSystemWindow ? type : 0, outInputChannel);
+                    mCanAddInternalSystemWindow ? type : 0, focusGrantToken, outInputChannel);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 91c1374..7d06526 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6588,4 +6588,18 @@
                     mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);
         }
     }
+
+    @Override
+    void updateOverlayInsetsState(WindowState originalChange) {
+        super.updateOverlayInsetsState(originalChange);
+        if (originalChange != getTopVisibleAppMainWindow()) {
+            return;
+        }
+        if (mOverlayHost != null) {
+            final InsetsState s = getDisplayContent().getInsetsPolicy()
+                .getInsetsForWindow(originalChange, true);
+            getBounds(mTmpRect);
+            mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 08130b6..1bd153b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -81,6 +81,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
+import android.view.InsetsState;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
@@ -313,7 +314,7 @@
 
     private final List<WindowContainerListener> mListeners = new ArrayList<>();
 
-    private OverlayHost mOverlayHost;
+    protected OverlayHost mOverlayHost;
 
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
@@ -3622,4 +3623,11 @@
             mOverlayHost = null;
         }
     }
+
+    void updateOverlayInsetsState(WindowState originalChange) {
+        final WindowContainer p = getParent();
+        if (p != null) {
+            p.updateOverlayInsetsState(originalChange);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ee0af9d..20fa7a9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -797,7 +797,12 @@
      * Callers prepare a view hierarchy with SurfaceControlViewHost
      * and send the package to WM here. The remote view hierarchy will receive
      * configuration change, lifecycle events, etc, forwarded over the
-     * ISurfaceControlViewHost interface inside the SurfacePackage.
+     * ISurfaceControlViewHost interface inside the SurfacePackage. Embedded
+     * hierarchies will receive inset changes, including transient inset changes
+     * (to avoid the status bar in immersive mode).
+     *
+     * The embedded hierarchy exists in a coordinate space relative to the task
+     * bounds.
      */
     public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
     public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 056b0ed..b37cb4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8303,7 +8303,8 @@
      */
     void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
                            SurfaceControl surface, IWindow window, IBinder hostInputToken,
-                           int flags, int privateFlags, int type, InputChannel outInputChannel) {
+                           int flags, int privateFlags, int type, IBinder focusGrantToken,
+                           InputChannel outInputChannel) {
         final InputApplicationHandle applicationHandle;
         final String name;
         final InputChannel clientChannel;
@@ -8311,7 +8312,7 @@
             EmbeddedWindowController.EmbeddedWindow win =
                     new EmbeddedWindowController.EmbeddedWindow(session, this, window,
                             mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type,
-                            displayId);
+                            displayId, focusGrantToken);
             clientChannel = win.openInputChannel();
             mEmbeddedWindowController.add(clientChannel.getToken(), win);
             applicationHandle = win.getApplicationHandle();
@@ -8590,10 +8591,10 @@
         }
     }
 
-    void grantEmbeddedWindowFocus(Session session, IBinder inputToken, boolean grantFocus) {
+    void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) {
         synchronized (mGlobalLock) {
             final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                    mEmbeddedWindowController.get(inputToken);
+                    mEmbeddedWindowController.getByFocusToken(focusToken);
             if (embeddedWindow == null) {
                 Slog.e(TAG, "Embedded window not found");
                 return;
@@ -8602,6 +8603,11 @@
                 Slog.e(TAG, "Window not in session:" + session);
                 return;
             }
+            IBinder inputToken = embeddedWindow.getInputChannelToken();
+            if (inputToken == null) {
+                Slog.e(TAG, "Focus token found but input channel token not found");
+                return;
+            }
             SurfaceControl.Transaction t = mTransactionFactory.get();
             final int displayId = embeddedWindow.mDisplayId;
             if (grantFocus) {
@@ -8631,7 +8637,7 @@
         }
     }
 
-    void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetInputToken,
+    void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetFocusToken,
                                   boolean grantFocus) {
         synchronized (mGlobalLock) {
             final WindowState hostWindow =
@@ -8645,7 +8651,7 @@
                 return;
             }
             final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
-                    mEmbeddedWindowController.get(targetInputToken);
+                    mEmbeddedWindowController.getByFocusToken(targetFocusToken);
             if (embeddedWindow == null) {
                 Slog.e(TAG, "Embedded window not found");
                 return;
@@ -8656,7 +8662,7 @@
             }
             SurfaceControl.Transaction t = mTransactionFactory.get();
             if (grantFocus) {
-                t.requestFocusTransfer(targetInputToken, embeddedWindow.toString(),
+                t.requestFocusTransfer(embeddedWindow.getInputChannelToken(), embeddedWindow.toString(),
                         hostWindow.mInputChannel.getToken(),
                         hostWindow.getName(),
                         hostWindow.getDisplayId()).apply();
@@ -8665,7 +8671,7 @@
                         "reason=grantEmbeddedWindowFocus(true)");
             } else {
                 t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
-                        targetInputToken,
+                        embeddedWindow.getInputChannelToken(),
                         embeddedWindow.toString(),
                         hostWindow.getDisplayId()).apply();
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8864b98..0a02b44 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3848,6 +3848,10 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to deliver inset state change w=" + this, e);
         }
+        final WindowContainer p = getParent();
+        if (p != null) {
+            p.updateOverlayInsetsState(this);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 6d8203c..db231f6 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
@@ -37,20 +36,15 @@
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
-import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
 import android.view.DisplayInfo;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
-import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.window.WindowContext;
 
@@ -485,9 +479,6 @@
      * This should only be called when {@link #mFixedRotationTransformState} is non-null.
      */
     private void onFixedRotationStatePrepared() {
-        // Send the adjustment info first so when the client receives configuration change, it can
-        // get the rotated display metrics.
-        notifyFixedRotationTransform(true /* enabled */);
         // Resolve the rotated configuration.
         onConfigurationChanged(getParent().getConfiguration());
         final ActivityRecord r = asActivityRecord();
@@ -543,7 +534,6 @@
         for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
             final WindowToken token = state.mAssociatedTokens.get(i);
             token.mFixedRotationTransformState = null;
-            token.notifyFixedRotationTransform(false /* enabled */);
             if (applyDisplayRotation == null) {
                 // Notify cancellation because the display does not change rotation.
                 token.cancelFixedRotationTransform();
@@ -551,44 +541,6 @@
         }
     }
 
-    /** Notifies application side to enable or disable the rotation adjustment of display info. */
-    void notifyFixedRotationTransform(boolean enabled) {
-        FixedRotationAdjustments adjustments = null;
-        // A token may contain windows of the same processes or different processes. The list is
-        // used to avoid sending the same adjustments to a process multiple times.
-        ArrayList<WindowProcessController> notifiedProcesses = null;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = mChildren.get(i);
-            final WindowProcessController app;
-            if (w.mAttrs.type == TYPE_APPLICATION_STARTING) {
-                // Use the host activity because starting window is controlled by window manager.
-                final ActivityRecord r = asActivityRecord();
-                if (r == null) {
-                    continue;
-                }
-                app = r.app;
-            } else {
-                app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid);
-            }
-            if (app == null || !app.hasThread()) {
-                continue;
-            }
-            if (notifiedProcesses == null) {
-                notifiedProcesses = new ArrayList<>(2);
-                adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null;
-            } else if (notifiedProcesses.contains(app)) {
-                continue;
-            }
-            notifiedProcesses.add(app);
-            try {
-                mWmService.mAtmService.getLifecycleManager().scheduleTransaction(
-                        app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments));
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e);
-            }
-        }
-    }
-
     /** Restores the changes that applies to this container. */
     private void cancelFixedRotationTransform() {
         final WindowContainer<?> parent = getParent();
@@ -609,15 +561,6 @@
     void onCancelFixedRotationTransform(int originalDisplayRotation) {
     }
 
-    FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() {
-        if (!isFixedRotationTransforming()) {
-            return null;
-        }
-        final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
-        return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
-                displayInfo.appHeight, displayInfo.displayCutout);
-    }
-
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
         super.resolveOverrideConfiguration(newParentConfig);
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 5178132..f8a8168 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -16,20 +16,18 @@
 
 #define LOG_TAG "NetworkStatsNative"
 
+#include <cutils/qtaguid.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "core_jni_helpers.h"
 #include <jni.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <utils/misc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <utils/Log.h>
+#include <utils/misc.h>
 
-#include "android-base/unique_fd.h"
 #include "bpf/BpfUtils.h"
 #include "netdbpf/BpfNetworkStats.h"
 
@@ -104,10 +102,15 @@
     }
 }
 
+static int deleteTagData(JNIEnv* /* env */, jclass /* clazz */, jint uid) {
+    return qtaguid_deleteTagData(0, uid);
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
         {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
         {"nativeGetUidStat", "(II)J", (void*)getUidStat},
+        {"nativeDeleteTagData", "(I)I", (void*)deleteTagData},
 };
 
 int register_android_server_net_NetworkStatsService(JNIEnv* env) {
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 574dbfd..79d8036 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -38,6 +38,10 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
+                <xs:element type="thermalThrottling" name="thermalThrottling">
+                    <xs:annotation name="nonnull"/>
+                    <xs:annotation name="final"/>
+                </xs:element>
                 <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
                             maxOccurs="1"/>
                 <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
@@ -154,6 +158,37 @@
         </xs:restriction>
     </xs:simpleType>
 
+    <xs:complexType name="thermalThrottling">
+        <xs:complexType>
+            <xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:complexType>
+    </xs:complexType>
+
+    <xs:complexType name="brightnessThrottlingMap">
+        <xs:sequence>
+            <xs:element name="brightnessThrottlingPoint" type="brightnessThrottlingPoint" maxOccurs="unbounded" minOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="brightnessThrottlingPoint">
+        <xs:sequence>
+            <xs:element type="thermalStatus" name="thermalStatus">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element type="nonNegativeDecimal" name="brightness">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
     <xs:complexType name="nitsMap">
         <xs:sequence>
             <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 04f0916..0b7df4d 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,6 +7,19 @@
     method public final void setMinimum(@NonNull java.math.BigDecimal);
   }
 
+  public class BrightnessThrottlingMap {
+    ctor public BrightnessThrottlingMap();
+    method @NonNull public final java.util.List<com.android.server.display.config.BrightnessThrottlingPoint> getBrightnessThrottlingPoint();
+  }
+
+  public class BrightnessThrottlingPoint {
+    ctor public BrightnessThrottlingPoint();
+    method @NonNull public final java.math.BigDecimal getBrightness();
+    method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus();
+    method public final void setBrightness(@NonNull java.math.BigDecimal);
+    method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus);
+  }
+
   public class Density {
     ctor public Density();
     method @NonNull public final java.math.BigInteger getDensity();
@@ -39,6 +52,7 @@
     method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+    method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
     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);
@@ -54,6 +68,7 @@
     method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal);
     method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal);
     method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal);
+    method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
   }
 
   public class DisplayQuirks {
@@ -131,6 +146,12 @@
     enum_constant public static final com.android.server.display.config.ThermalStatus shutdown;
   }
 
+  public class ThermalThrottling {
+    ctor public ThermalThrottling();
+    method @NonNull public final com.android.server.display.config.BrightnessThrottlingMap getBrightnessThrottlingMap();
+    method public final void setBrightnessThrottlingMap(@NonNull com.android.server.display.config.BrightnessThrottlingMap);
+  }
+
   public class Thresholds {
     ctor public Thresholds();
     method @NonNull public final com.android.server.display.config.BrightnessThresholds getBrighteningThresholds();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7fa0f6a..74e04ed 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -183,6 +183,7 @@
 import com.android.server.security.FileIntegrityService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
+import com.android.server.sensorprivacy.SensorPrivacyService;
 import com.android.server.sensors.SensorService;
 import com.android.server.signedconfig.SignedConfigService;
 import com.android.server.soundtrigger.SoundTriggerService;
@@ -1454,6 +1455,10 @@
             mSystemServiceManager.startService(KeyChainSystemService.class);
             t.traceEnd();
 
+            t.traceBegin("StartBinaryTransparencyService");
+            mSystemServiceManager.startService(BinaryTransparencyService.class);
+            t.traceEnd();
+
             t.traceBegin("StartSchedulingPolicyService");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
             t.traceEnd();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index d562786..717168f 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -90,6 +90,11 @@
 
     private static final String TAG = "MidiService";
 
+    // These limits are much higher than any normal app should need.
+    private static final int MAX_DEVICE_SERVERS_PER_UID = 16;
+    private static final int MAX_LISTENERS_PER_CLIENT = 16;
+    private static final int MAX_CONNECTIONS_PER_CLIENT = 64;
+
     private final Context mContext;
 
     // list of all our clients, keyed by Binder token
@@ -161,6 +166,10 @@
         }
 
         public void addListener(IMidiDeviceListener listener) {
+            if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) {
+                throw new SecurityException(
+                        "too many MIDI listeners for UID = " + mUid);
+            }
             // Use asBinder() so that we can match it in removeListener().
             // The listener proxy objects themselves do not match.
             mListeners.put(listener.asBinder(), listener);
@@ -174,6 +183,10 @@
         }
 
         public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
+            if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) {
+                throw new SecurityException(
+                        "too many MIDI connections for UID = " + mUid);
+            }
             DeviceConnection connection = new DeviceConnection(device, this, callback);
             mDeviceConnections.put(connection.getToken(), connection);
             device.addDeviceConnection(connection);
@@ -902,6 +915,19 @@
             IMidiDeviceServer server, ServiceInfo serviceInfo,
             boolean isPrivate, int uid, int defaultProtocol) {
 
+        // Limit the number of devices per app.
+        int deviceCountForApp = 0;
+        for (Device device : mDevicesByInfo.values()) {
+            if (device.getUid() == uid) {
+                deviceCountForApp++;
+            }
+        }
+        if (deviceCountForApp >= MAX_DEVICE_SERVERS_PER_UID) {
+            throw new SecurityException(
+                    "too many MIDI devices already created for UID = "
+                    + uid);
+        }
+
         int id = mNextDeviceId++;
         MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
                 inputPortNames, outputPortNames, properties, isPrivate,
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
index ced24e0..ae4227b 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
@@ -19,8 +19,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.os.IBinder;
 import android.service.selectiontoolbar.ISelectionToolbarRenderService;
 import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.util.Slog;
 import android.view.selectiontoolbar.ISelectionToolbarCallback;
 import android.view.selectiontoolbar.ShowInfo;
 
@@ -35,12 +37,14 @@
             AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
 
     private final ComponentName mComponentName;
+    private final IBinder mRemoteCallback;
 
-
-    RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId) {
+    RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId,
+            IBinder callback) {
         super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE).setComponent(
                 serviceName), 0, userId, ISelectionToolbarRenderService.Stub::asInterface);
         mComponentName = serviceName;
+        mRemoteCallback = callback;
         // Bind right away.
         connect();
     }
@@ -50,19 +54,31 @@
         return TIMEOUT_IDLE_UNBIND_MS;
     }
 
+    @Override // from ServiceConnector.Impl
+    protected void onServiceConnectionStatusChanged(ISelectionToolbarRenderService service,
+            boolean connected) {
+        try {
+            if (connected) {
+                service.onConnected(mRemoteCallback);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception calling onConnected().", e);
+        }
+    }
+
     public ComponentName getComponentName() {
         return mComponentName;
     }
 
-    public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
-        run((s) -> s.onShow(showInfo, callback));
+    public void onShow(int callingUid, ShowInfo showInfo, ISelectionToolbarCallback callback) {
+        run((s) -> s.onShow(callingUid, showInfo, callback));
     }
 
     public void onHide(long widgetToken) {
         run((s) -> s.onHide(widgetToken));
     }
 
-    public void onDismiss(long widgetToken) {
-        run((s) -> s.onDismiss(widgetToken));
+    public void onDismiss(int callingUid, long widgetToken) {
+        run((s) -> s.onDismiss(callingUid, widgetToken));
     }
 }
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 235f547..525a931 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -23,12 +23,17 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.hardware.input.InputManagerInternal;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.selectiontoolbar.ISelectionToolbarRenderServiceCallback;
 import android.util.Slog;
 import android.view.selectiontoolbar.ISelectionToolbarCallback;
 import android.view.selectiontoolbar.ShowInfo;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
 import com.android.server.infra.AbstractPerUserSystemService;
 
 final class SelectionToolbarManagerServiceImpl extends
@@ -41,9 +46,14 @@
     @Nullable
     private RemoteSelectionToolbarRenderService mRemoteService;
 
+    InputManagerInternal mInputManagerInternal;
+    private final SelectionToolbarRenderServiceRemoteCallback mRemoteServiceCallback =
+            new SelectionToolbarRenderServiceRemoteCallback();
+
     protected SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
             @NonNull Object lock, int userId) {
         super(master, lock, userId);
+        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         updateRemoteServiceLocked();
     }
 
@@ -78,7 +88,7 @@
     void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
         final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
         if (remoteService != null) {
-            remoteService.onShow(showInfo, callback);
+            remoteService.onShow(Binder.getCallingUid(), showInfo, callback);
         }
     }
 
@@ -94,7 +104,7 @@
     void dismissToolbar(long widgetToken) {
         final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
         if (remoteService != null) {
-            remoteService.onDismiss(widgetToken);
+            remoteService.onDismiss(Binder.getCallingUid(), widgetToken);
         }
     }
 
@@ -105,7 +115,7 @@
             final String serviceName = getComponentNameLocked();
             final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
             mRemoteService = new RemoteSelectionToolbarRenderService(getContext(), serviceComponent,
-                    mUserId);
+                    mUserId, mRemoteServiceCallback);
         }
         return mRemoteService;
     }
@@ -125,4 +135,17 @@
         }
         return si;
     }
+
+    private void transferTouchFocus(IBinder source, IBinder target) {
+        mInputManagerInternal.transferTouchFocus(source, target);
+    }
+
+    private final class SelectionToolbarRenderServiceRemoteCallback extends
+            ISelectionToolbarRenderServiceCallback.Stub {
+
+        @Override
+        public void transferTouch(IBinder source, IBinder target) {
+            transferTouchFocus(source, target);
+        }
+    }
 }
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 5220c8f..c5709fc 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -19,6 +19,7 @@
 import android.app.PropertyInvalidatedCache
 import android.content.ComponentName
 import android.content.Context
+import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import com.android.server.pm.pkg.component.ParsedActivity
 import android.os.Binder
@@ -167,12 +168,12 @@
     @Parameterized.Parameter(0)
     lateinit var params: Params
 
-    private lateinit var testHandler: TestHandler
     private lateinit var mockPendingBroadcasts: PendingPackageBroadcasts
     private lateinit var mockPkg: AndroidPackage
     private lateinit var mockPkgSetting: PackageSetting
     private lateinit var service: PackageManagerService
 
+    private val testHandler = TestHandler(null)
     private val userId = UserHandle.getCallingUserId()
     private val userIdDifferent = userId + 1
 
@@ -180,16 +181,16 @@
     fun setUpMocks() {
         makeTestData()
 
-        testHandler = TestHandler(null)
+        mockPendingBroadcasts = PendingPackageBroadcasts()
+        service = mockService()
+
+        testHandler.clear()
+
         if (params.result is Result.ChangedWithoutNotify) {
             // Case where the handler already has a message and so another should not be sent.
             // This case will verify that only 1 message exists, which is the one added here.
             testHandler.sendEmptyMessage(SEND_PENDING_BROADCAST)
         }
-
-        mockPendingBroadcasts = PendingPackageBroadcasts()
-
-        service = mockService()
     }
 
     @Test
@@ -201,19 +202,22 @@
         when (val result = params.result) {
             Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> {
                 runUpdate()
-                verify(mockPkgSetting).overrideNonLocalizedLabelAndIcon(params.componentName!!,
-                        TEST_LABEL, TEST_ICON, userId)
+                mockPkgSetting.getUserStateOrDefault(userId)
+                    .getOverrideLabelIconForComponent(params.componentName!!)
+                    .let {
+                        assertThat(it?.first).isEqualTo(TEST_LABEL)
+                        assertThat(it?.second).isEqualTo(TEST_ICON)
+                    }
             }
             is Result.Exception -> {
                 assertThrows(result.type) { runUpdate() }
-                verify(mockPkgSetting, never()).overrideNonLocalizedLabelAndIcon(
-                        any<ComponentName>(), any(), anyInt(), anyInt())
             }
         }
     }
 
     @After
     fun verifyExpectedResult() {
+        assertServiceInitialized() ?: return
         if (params.componentName != null) {
             val activityInfo = service.getActivityInfo(params.componentName, 0, userId)
             if (activityInfo != null) {
@@ -225,11 +229,14 @@
 
     @After
     fun verifyDifferentUserUnchanged() {
+        assertServiceInitialized() ?: return
         when (params.result) {
             Result.Changed, Result.ChangedWithoutNotify -> {
-                val activityInfo = service.getActivityInfo(params.componentName, 0, userIdDifferent)
-                assertThat(activityInfo.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
-                assertThat(activityInfo.icon).isEqualTo(DEFAULT_ICON)
+                // Suppress so that failures in @After don't override the actual test failure
+                @Suppress("UNNECESSARY_SAFE_CALL")
+                val activityInfo = service?.getActivityInfo(params.componentName, 0, userIdDifferent)
+                assertThat(activityInfo?.nonLocalizedLabel).isEqualTo(DEFAULT_LABEL)
+                assertThat(activityInfo?.icon).isEqualTo(DEFAULT_ICON)
             }
             Result.NotChanged, is Result.Exception -> {}
         }.run { /*exhaust*/ }
@@ -237,6 +244,7 @@
 
     @After
     fun verifyHandlerHasMessage() {
+        assertServiceInitialized() ?: return
         when (params.result) {
             is Result.Changed, is Result.ChangedWithoutNotify -> {
                 assertThat(testHandler.pendingMessages).hasSize(1)
@@ -251,9 +259,10 @@
 
     @After
     fun verifyPendingBroadcast() {
+        assertServiceInitialized() ?: return
         when (params.result) {
             is Result.Changed, Result.ChangedWithoutNotify -> {
-                assertThat(mockPendingBroadcasts.get(userId, params.pkgName))
+                assertThat(mockPendingBroadcasts.get(userId, params.pkgName) ?: emptyList<String>())
                         .containsExactly(params.componentName!!.className)
                         .inOrder()
             }
@@ -271,26 +280,27 @@
                     .apply(block)
                     .hideAsFinal()
 
-    private fun makePkgSetting(pkgName: String) = spy(
+    private fun makePkgSetting(pkgName: String, pkg: AndroidPackage) =
         PackageSetting(
             pkgName, null, File("/test"),
             null, null, null, null, 0, 0, 0, 0, null, null, null, null, null,
             UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
-        )
-    ) {
-        this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
-    }
+        ).apply {
+            if (params.isSystem) {
+                this.flags = this.flags or ApplicationInfo.FLAG_SYSTEM
+            }
+            this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
+            this.pkg = pkg
+        }
 
     private fun makeTestData() {
         mockPkg = makePkg(params.pkgName)
-        mockPkgSetting = makePkgSetting(params.pkgName)
+        mockPkgSetting = makePkgSetting(params.pkgName, mockPkg)
 
         if (params.result is Result.NotChanged) {
             // If verifying no-op behavior, set the current setting to the test values
             mockPkgSetting.overrideNonLocalizedLabelAndIcon(params.componentName!!, TEST_LABEL,
                     TEST_ICON, userId)
-            // Then clear the mock because the line above just incremented it
-            clearInvocations(mockPkgSetting)
         }
     }
 
@@ -303,9 +313,9 @@
                 INVALID_PKG to makePkg(INVALID_PKG) { uid = Binder.getCallingUid() + 1 }
         )
         val mockedPkgSettings = mutableMapOf(
-                VALID_PKG to makePkgSetting(VALID_PKG),
-                SHARED_PKG to makePkgSetting(SHARED_PKG),
-                INVALID_PKG to makePkgSetting(INVALID_PKG)
+                VALID_PKG to makePkgSetting(VALID_PKG, mockedPkgs[VALID_PKG]!!),
+                SHARED_PKG to makePkgSetting(SHARED_PKG, mockedPkgs[SHARED_PKG]!!),
+                INVALID_PKG to makePkgSetting(INVALID_PKG, mockedPkgs[INVALID_PKG]!!)
         )
 
         var mockActivity: ParsedActivity? = null
@@ -330,6 +340,8 @@
                 whenever(this.componentExists(same(it))) { mockActivity != null }
                 whenever(this.getActivity(same(it))) { mockActivity }
             }
+            whenever(this.snapshot()) { this@mockThrowOnUnmocked }
+            whenever(registerObserver(any())).thenCallRealMethod()
         }
         val mockUserManagerService: UserManagerService = mockThrowOnUnmocked {
             val matcher: (Int) -> Boolean = { it == userId || it == userIdDifferent }
@@ -345,6 +357,8 @@
         val mockAppsFilter: AppsFilter = mockThrowOnUnmocked {
             whenever(this.shouldFilterApplication(anyInt(), any<PackageSetting>(),
                     any<PackageSetting>(), anyInt())) { false }
+            whenever(this.snapshot()) { this@mockThrowOnUnmocked }
+            whenever(registerObserver(any())).thenCallRealMethod()
         }
         val mockContext: Context = mockThrowOnUnmocked {
             whenever(this.getString(
@@ -354,27 +368,33 @@
                 PackageManager.PERMISSION_GRANTED
             }
         }
-        val mockSharedLibrariesImpl: SharedLibrariesImpl = mock()
+        val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
+            whenever(this.snapshot()) { this@mock }
+        }
         val mockInjector: PackageManagerServiceInjector = mock {
             whenever(this.lock) { PackageManagerTracedLock() }
             whenever(this.componentResolver) { mockComponentResolver }
             whenever(this.userManagerService) { mockUserManagerService }
-            whenever(this.getUserManagerInternal()) { mockUserManagerInternal }
+            whenever(this.userManagerInternal) { mockUserManagerInternal }
             whenever(this.settings) { mockSettings }
             whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) {
                 mockActivityTaskManager
             }
             whenever(this.appsFilter) { mockAppsFilter }
             whenever(this.context) { mockContext }
-            whenever(this.getHandler()) { testHandler }
+            whenever(this.handler) { testHandler }
             whenever(this.sharedLibrariesImpl) { mockSharedLibrariesImpl }
         }
         val testParams = PackageManagerServiceTestParams().apply {
             this.pendingPackageBroadcasts = mockPendingBroadcasts
             this.resolveComponentName = ComponentName("android", ".Test")
             this.packages = ArrayMap<String, AndroidPackage>().apply { putAll(mockedPkgs) }
+            this.instantAppRegistry = mock()
         }
 
         return PackageManagerService(mockInjector, testParams)
     }
+
+    // If service isn't initialized, then test setup failed and @Afters should be skipped
+    private fun assertServiceInitialized() = Unit.takeIf { ::service.isInitialized }
 }
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
index a4de08a..4e0bb36 100644
--- a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
@@ -1,7 +1,7 @@
 <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
 <sensor-privacy persistence-version="1" version="1">
     <user id="0" enabled="false">
-        <individual-sensor-privacy sensor="1" enabled="true" />
-        <individual-sensor-privacy sensor="2" enabled="true" />
+        <individual-sensor-privacy sensor="1" enabled="true" last-change="100" />
+        <individual-sensor-privacy sensor="2" enabled="true" last-change="100" />
     </user>
 </sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml
new file mode 100644
index 0000000..2d192db
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file7.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="2" version="2">
+    <sensor-state toggle-type="1" user-id="0" sensor="1" state-type="1" last-change="123" />
+    <sensor-state toggle-type="1" user-id="0" sensor="2" state-type="2" last-change="123" />
+</sensor-privacy>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml
new file mode 100644
index 0000000..7bb38b4
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file8.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="2" version="2">
+    <sensor-state toggle-type="2" user-id="0" sensor="1" state-type="1" last-change="1234" />
+    <sensor-state toggle-type="2" user-id="0" sensor="2" state-type="2" last-change="1234" />
+</sensor-privacy>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 303f955..bf46f55 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -97,7 +97,7 @@
 
     @Before
     public void setUp() {
-        System.loadLibrary("activitymanagermockingservicestestjni");
+        System.loadLibrary("mockingservicestestjni");
         mHandlerThread = new HandlerThread("");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 0198253..eed2d42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -35,6 +35,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -45,6 +46,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.SystemService;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -167,7 +170,8 @@
     }
 
     private void startUser(GameManagerService gameManagerService, int userId) {
-        gameManagerService.onUserStarting(userId);
+        UserInfo userInfo = new UserInfo(userId, "name", 0);
+        gameManagerService.onUserStarting(new SystemService.TargetUser(userInfo));
         mTestLooper.dispatchAll();
     }
 
@@ -861,7 +865,7 @@
     public void testInterventionAllowAngleFalse() throws Exception {
         GameManagerService gameManagerService =
                 new GameManagerService(mMockContext, mTestLooper.getLooper());
-        gameManagerService.onUserStarting(USER_ID_1);
+        startUser(gameManagerService, USER_ID_1);
         mockDeviceConfigPerformanceEnableAngle();
         mockInterventionAllowAngleFalse();
         mockModifyGameModeGranted();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
index 0545fde..1c480ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
@@ -207,12 +207,12 @@
     }
 
     private void seedNoConfigurationForUser(SystemService.TargetUser user) {
-        when(mMockGameServiceProviderSelector.get(user)).thenReturn(null);
+        when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(null);
     }
 
     private FakeGameServiceProviderInstance seedConfigurationForUser(SystemService.TargetUser user,
             GameServiceProviderConfiguration configuration) {
-        when(mMockGameServiceProviderSelector.get(user)).thenReturn(configuration);
+        when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(configuration);
         FakeGameServiceProviderInstance instanceForConfiguration =
                 spy(new FakeGameServiceProviderInstance());
         when(mMockGameServiceProviderInstanceFactory.create(configuration))
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
index 59d0970..23a6a49 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.app;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -25,7 +26,6 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -138,7 +138,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(null);
+                mGameServiceProviderSelector.get(null, null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -155,7 +155,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(managedTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(managedTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -171,7 +171,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -187,7 +187,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -201,7 +201,7 @@
         seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -218,7 +218,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -234,7 +234,7 @@
                 "res/xml/game_service_metadata_wrong_first_tag.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         assertThat(gameServiceProviderConfiguration).isNull();
     }
@@ -251,7 +251,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
                 new GameServiceProviderConfiguration(USER_HANDLE_10,
@@ -277,7 +277,7 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
         GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
                 new GameServiceProviderConfiguration(USER_HANDLE_10,
@@ -300,7 +300,31 @@
                 "res/xml/game_service_metadata_valid.xml");
 
         GameServiceProviderConfiguration gameServiceProviderConfiguration =
-                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10));
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
+
+        GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
+                new GameServiceProviderConfiguration(USER_HANDLE_10,
+                        GAME_SERVICE_COMPONENT,
+                        GAME_SESSION_SERVICE_COMPONENT);
+        assertThat(gameServiceProviderConfiguration).isEqualTo(
+                expectedGameServiceProviderConfiguration);
+    }
+
+    @Test
+    public void get_overridePresent_returnsDeviceConfigGameServiceProvider()
+            throws Exception {
+        seedSystemGameServicePackageName("other.package");
+
+        seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
+                resolveInfo(GAME_SERVICE_B_SERVICE_INFO), resolveInfo(GAME_SERVICE_SERVICE_INFO));
+        seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
+        seedGameServiceMetaDataFromFile(GAME_SERVICE_PACKAGE_NAME,
+                GAME_SERVICE_META_DATA_RES_ID,
+                "res/xml/game_service_metadata_valid.xml");
+
+        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+                mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10),
+                        GAME_SERVICE_PACKAGE_NAME);
 
         GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
                 new GameServiceProviderConfiguration(USER_HANDLE_10,
@@ -324,7 +348,7 @@
                         argThat(intent ->
                                 intent != null
                                         && intent.getAction().equals(
-                                                GameService.ACTION_GAME_SERVICE)
+                                        GameService.ACTION_GAME_SERVICE)
                                         && intent.getPackage().equals(gameServicePackageName)
                         ),
                         anyInt(),
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 1e0f30e..6ae0031 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -169,24 +169,6 @@
             mSettingsMap.putAll(mPreExistingSettings)
             !mPreExistingSettings.isEmpty()
         }
-        whenever(mocks.settings.setPackageStoppedStateLPw(any(), any(), anyBoolean(), anyInt())) {
-            val pm: PackageManagerService = getArgument(0)
-            val pkgSetting = mSettingsMap[getArgument(1)]!!
-            val stopped: Boolean = getArgument(2)
-            val userId: Int = getArgument(3)
-            return@whenever if (pkgSetting.getStopped(userId) != stopped) {
-                pkgSetting.setStopped(stopped, userId)
-                if (pkgSetting.getNotLaunched(userId)) {
-                    pkgSetting.installSource.installerPackageName?.let {
-                        pm.notifyFirstLaunch(pkgSetting.packageName, it, userId)
-                    }
-                    pkgSetting.setNotLaunched(false, userId)
-                }
-                true
-            } else {
-                false
-            }
-        }
     }
 
     /** Collection of mocks used for PackageManagerService tests. */
@@ -210,7 +192,9 @@
         val packageParser: PackageParser2 = mock()
         val keySetManagerService: KeySetManagerService = mock()
         val packageAbiHelper: PackageAbiHelper = mock()
-        val appsFilter: AppsFilter = mock()
+        val appsFilter: AppsFilter = mock {
+            whenever(snapshot()) { this@mock }
+        }
         val dexManager: DexManager = mock()
         val installer: Installer = mock()
         val displayMetrics: DisplayMetrics = mock()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
index edbfecc..ccfeb4c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
@@ -59,8 +59,7 @@
             false /*isEngBuild*/,
             false /*isUserDebugBuild*/,
             Build.VERSION_CODES.CUR_DEVELOPMENT,
-            Build.VERSION.INCREMENTAL,
-            false /*snapshotEnabled*/)
+            Build.VERSION.INCREMENTAL)
         rule.system().validateFinalState()
         return pms
     }
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 0820a3c..bbca121 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -63,8 +63,7 @@
                 false /*isEngBuild*/,
                 false /*isUserDebugBuild*/,
                 Build.VERSION_CODES.CUR_DEVELOPMENT,
-                Build.VERSION.INCREMENTAL,
-                false /*snapshotEnabled*/)
+                Build.VERSION.INCREMENTAL)
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index cfc81e6..a6c7bfb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -170,7 +170,6 @@
             false /*isEngBuild*/,
             false /*isUserDebugBuild*/,
             Build.VERSION_CODES.CUR_DEVELOPMENT,
-            Build.VERSION.INCREMENTAL,
-            false /*snapshotEnabled*/)
+            Build.VERSION.INCREMENTAL)
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index 2735e3d..b89f36f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -24,6 +24,7 @@
 import android.os.storage.StorageManager
 import android.util.ArrayMap
 import android.util.PackageUtils
+import com.android.internal.util.FunctionalUtils
 import com.android.server.SystemConfig.SharedLibraryEntry
 import com.android.server.compat.PlatformCompat
 import com.android.server.extendedtestutils.wheneverStatic
@@ -34,6 +35,7 @@
 import com.android.server.testutils.any
 import com.android.server.testutils.eq
 import com.android.server.testutils.mock
+import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.nullable
 import com.android.server.testutils.spy
 import com.android.server.testutils.whenever
@@ -41,11 +43,14 @@
 import com.google.common.truth.Truth.assertThat
 import libcore.util.HexEncoding
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.mockito.Mock
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -96,10 +101,14 @@
         mRule.system().stageNominalSystemState()
         addExistingPackages()
 
-        val testParams = PackageManagerServiceTestParams().apply {
-            packages = mExistingPackages
-        }
-        mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
+        mPms = spy(PackageManagerService(mRule.mocks().injector,
+            false /*coreOnly*/,
+            false /*factoryTest*/,
+            MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+            false /*isEngBuild*/,
+            false /*isUserDebugBuild*/,
+            Build.VERSION_CODES.CUR_DEVELOPMENT,
+            Build.VERSION.INCREMENTAL))
         mSettings = mRule.mocks().injector.settings
         mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
         mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
@@ -109,7 +118,19 @@
         whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
             .thenReturn(mStorageManager)
         whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
-        doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
+        doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageName(any(), any())
+        doAnswer {
+            it.getArgument<FunctionalUtils.ThrowingConsumer<Computer>>(0).acceptOrThrow(
+                mockThrowOnUnmocked {
+                    whenever(sharedLibraries) { mSharedLibrariesImpl.sharedLibraries }
+                    whenever(resolveInternalPackageName(anyString(), anyLong())) {
+                        mPms.resolveInternalPackageName(getArgument(0), getArgument(1))
+                    }
+                    whenever(getPackageStateInternal(anyString())) {
+                        mPms.getPackageStateInternal(getArgument(0))
+                    }
+                })
+        }.`when`(mPms).executeWithConsistentComputer(any())
         whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
             .thenReturn(PackageManager.DELETE_SUCCEEDED)
         whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
@@ -232,6 +253,7 @@
         assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
     }
 
+    @Ignore("b/216603387")
     @Test
     fun updateSharedLibraries_withStaticLibPackage() {
         val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
@@ -244,6 +266,7 @@
         assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
     }
 
+    @Ignore("b/216603387")
     @Test
     fun updateSharedLibraries_withConsumerPackage() {
         val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
@@ -257,6 +280,7 @@
         assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
     }
 
+    @Ignore("b/216603387")
     @Test
     fun updateAllSharedLibraries() {
         mExistingSettings.forEach {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index fe7e2d9..ac406b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -128,7 +128,7 @@
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
 
-        verify(pms).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
         verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
             nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
             nullable(), nullable(), nullable())
@@ -214,7 +214,7 @@
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
 
-        verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
         verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
             nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
             nullable(), nullable(), nullable())
@@ -295,7 +295,7 @@
             { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID)
 
         testHandler.flush()
-        verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+        verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
         verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
             nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
             nullable(), nullable(), nullable())
@@ -499,8 +499,7 @@
                 false /*isEngBuild*/,
                 false /*isUserDebugBuild*/,
                 Build.VERSION_CODES.CUR_DEVELOPMENT,
-                Build.VERSION.INCREMENTAL,
-                false /*snapshotEnabled*/)
+                Build.VERSION.INCREMENTAL)
         rule.system().validateFinalState()
         return pms
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 38f01b5..64e8613 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -16,43 +16,47 @@
 
 package com.android.server.sensorprivacy;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
+import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AppOpsManager;
-import android.app.AppOpsManagerInternal;
 import android.content.Context;
-import android.content.pm.UserInfo;
+import android.hardware.SensorPrivacyManager;
 import android.os.Environment;
-import android.telephony.TelephonyManager;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.server.LocalServices;
-import com.android.server.SensorPrivacyService;
-import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
 
-import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
+import org.mockito.ArgumentCaptor;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.util.concurrent.CompletableFuture;
+import java.nio.file.StandardCopyOption;
 
 @RunWith(AndroidTestingRunner.class)
 public class SensorPrivacyServiceMockingTest {
@@ -71,6 +75,10 @@
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5);
     public static final String PERSISTENCE_FILE6 =
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
+    public static final String PERSISTENCE_FILE7 =
+            String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 7);
+    public static final String PERSISTENCE_FILE8 =
+            String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 8);
 
     public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE =
             "SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml";
@@ -81,176 +89,281 @@
     public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE =
             "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml";
 
-    private Context mContext;
-    @Mock
-    private AppOpsManager mMockedAppOpsManager;
-    @Mock
-    private AppOpsManagerInternal mMockedAppOpsManagerInternal;
-    @Mock
-    private UserManagerInternal mMockedUserManagerInternal;
-    @Mock
-    private ActivityManager mMockedActivityManager;
-    @Mock
-    private ActivityTaskManager mMockedActivityTaskManager;
-    @Mock
-    private TelephonyManager mMockedTelephonyManager;
+    Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    String mDataDir = mContext.getApplicationInfo().dataDir;
+
+    @Before
+    public void setUp() {
+        new File(mDataDir, "sensor_privacy.xml").delete();
+        new File(mDataDir, "sensor_privacy_impl.xml").delete();
+    }
 
     @Test
-    public void testServiceInit() throws IOException {
+    public void testMigration1() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
+
+        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+    }
+
+    @Test
+    public void testMigration2() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE2);
+
+        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+        assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+
+        assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+
+        assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
+        assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(HARDWARE, 11, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 11, CAMERA));
+        assertNull(ps.getState(HARDWARE, 12, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 12, CAMERA));
+    }
+
+    @Test
+    public void testMigration3() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE3);
+
+        assertFalse(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+    }
+
+    @Test
+    public void testMigration4() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE4);
+
+        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+        assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+    }
+
+    @Test
+    public void testMigration5() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE5);
+
+        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+    }
+
+    @Test
+    public void testMigration6() throws IOException {
+        PersistedState ps = migrateFromFile(PERSISTENCE_FILE6);
+
+        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+    }
+
+    private PersistedState migrateFromFile(String fileName) throws IOException {
         MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
                 .initMocks(this)
                 .strictness(Strictness.WARN)
                 .spyStatic(LocalServices.class)
                 .spyStatic(Environment.class)
                 .startMocking();
-
         try {
-            mContext = InstrumentationRegistry.getInstrumentation().getContext();
-            spyOn(mContext);
+            doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory());
 
-            doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
-            doReturn(mMockedUserManagerInternal)
-                    .when(() -> LocalServices.getService(UserManagerInternal.class));
-            doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
-            doReturn(mMockedActivityTaskManager)
-                    .when(mContext).getSystemService(ActivityTaskManager.class);
-            doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
-                    TelephonyManager.class);
+            UserManagerInternal umi = mock(UserManagerInternal.class);
+            doReturn(umi).when(() -> LocalServices.getService(UserManagerInternal.class));
+            doReturn(new int[] {0}).when(umi).getUserIds();
 
-            String dataDir = mContext.getApplicationInfo().dataDir;
-            doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
+            Files.copy(
+                    mContext.getAssets().open(fileName),
+                    new File(mDataDir, "sensor_privacy.xml").toPath(),
+                    StandardCopyOption.REPLACE_EXISTING);
 
-            File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
-            onDeviceFile.delete();
-
-            // Try all files with one known user
-            doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
-            doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
-                    .getUserInfo(0);
-            initServiceWithPersistenceFile(onDeviceFile, null);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
-
-            // Try all files with two known users
-            doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds();
-            doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
-                    .getUserInfo(0);
-            doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
-                    .getUserInfo(10);
-            initServiceWithPersistenceFile(onDeviceFile, null);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
-            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
-
+            return PersistedState.fromFile("sensor_privacy_impl.xml");
         } finally {
             mockitoSession.finishMocking();
         }
     }
 
     @Test
-    public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException {
-        testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true);
+    public void testPersistence1Version2() throws IOException {
+        PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7);
+
+        assertEquals(1, ps.getState(SOFTWARE, 0, MICROPHONE).getState());
+        assertEquals(123L, ps.getState(SOFTWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(SOFTWARE, 0, CAMERA).getState());
+        assertEquals(123L, ps.getState(SOFTWARE, 0, CAMERA).getLastChange());
+
+        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
     }
 
     @Test
-    public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException {
-        testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false);
+    public void testPersistence2Version2() throws IOException {
+        PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8);
+
+        assertEquals(1, ps.getState(HARDWARE, 0, MICROPHONE).getState());
+        assertEquals(1234L, ps.getState(HARDWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(HARDWARE, 0, CAMERA).getState());
+        assertEquals(1234L, ps.getState(HARDWARE, 0, CAMERA).getLastChange());
+
+        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(HARDWARE, 10, CAMERA));
     }
 
-    @Test
-    public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException {
-        testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true);
-    }
-
-    @Test
-    public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException {
-        testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false);
-    }
-
-    private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute,
-            boolean expectedMicState, boolean expectedCamState)
-            throws IOException {
+    private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
         MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
                 .initMocks(this)
                 .strictness(Strictness.WARN)
                 .spyStatic(LocalServices.class)
                 .spyStatic(Environment.class)
                 .startMocking();
-
         try {
-            mContext = InstrumentationRegistry.getInstrumentation().getContext();
-            spyOn(mContext);
+            doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory());
+            Files.copy(
+                    mContext.getAssets().open(version2FilePath),
+                    new File(mDataDir, "sensor_privacy_impl.xml").toPath(),
+                    StandardCopyOption.REPLACE_EXISTING);
 
-            doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
-            doReturn(mMockedAppOpsManagerInternal)
-                    .when(() -> LocalServices.getService(AppOpsManagerInternal.class));
-            doReturn(mMockedUserManagerInternal)
-                    .when(() -> LocalServices.getService(UserManagerInternal.class));
-            doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
-            doReturn(mMockedActivityTaskManager)
-                    .when(mContext).getSystemService(ActivityTaskManager.class);
-            doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
-                    TelephonyManager.class);
-
-            String dataDir = mContext.getApplicationInfo().dataDir;
-            doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
-
-            File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
-            onDeviceFile.delete();
-
-            doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
-            doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
-                    .getUserInfo(0);
-
-            CompletableFuture<Boolean> micState = new CompletableFuture<>();
-            CompletableFuture<Boolean> camState = new CompletableFuture<>();
-            doAnswer(invocation -> {
-                int code = invocation.getArgument(0);
-                boolean restricted = invocation.getArgument(1);
-                if (code == AppOpsManager.OP_RECORD_AUDIO) {
-                    micState.complete(restricted);
-                } else if (code == AppOpsManager.OP_CAMERA) {
-                    camState.complete(restricted);
-                }
-                return null;
-            }).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(),
-                    any());
-
-            initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0);
-
-            Assert.assertTrue(micState.join() == expectedMicState);
-            Assert.assertTrue(camState.join() == expectedCamState);
-
+            return PersistedState.fromFile("sensor_privacy_impl.xml");
         } finally {
             mockitoSession.finishMocking();
         }
     }
 
-    private void initServiceWithPersistenceFile(File onDeviceFile,
-            String persistenceFilePath) throws IOException {
-        initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1);
+    @Test
+    public void testGetDefaultState() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.WARN)
+                .spyStatic(PersistedState.class)
+                .startMocking();
+        try {
+            PersistedState persistedState = mock(PersistedState.class);
+            doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
+            doReturn(null).when(persistedState).getState(anyInt(), anyInt(), anyInt());
+
+            SensorPrivacyStateController sensorPrivacyStateController =
+                    getSensorPrivacyStateControllerImpl();
+
+            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+            SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+
+            assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState());
+            assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState());
+            verify(persistedState, times(1)).getState(SOFTWARE, 0, MICROPHONE);
+            verify(persistedState, times(1)).getState(SOFTWARE, 0, CAMERA);
+        } finally {
+            mockitoSession.finishMocking();
+        }
     }
 
-    private void initServiceWithPersistenceFile(File onDeviceFile,
-            String persistenceFilePath, int startingUserId) throws IOException {
-        if (persistenceFilePath != null) {
-            Files.copy(mContext.getAssets().open(persistenceFilePath),
-                    onDeviceFile.toPath());
+    @Test
+    public void testGetSetState() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.WARN)
+                .spyStatic(PersistedState.class)
+                .startMocking();
+        try {
+            PersistedState persistedState = mock(PersistedState.class);
+            SensorState sensorState = mock(SensorState.class);
+            doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
+            doReturn(sensorState).when(persistedState).getState(SOFTWARE, 0, MICROPHONE);
+            doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState();
+            doReturn(0L).when(sensorState).getLastChange();
+
+            SensorPrivacyStateController sensorPrivacyStateController =
+                    getSensorPrivacyStateControllerImpl();
+
+            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+
+            assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
+            assertEquals(0L, micState.getLastChange());
+        } finally {
+            mockitoSession.finishMocking();
         }
-        SensorPrivacyService service = new SensorPrivacyService(mContext);
-        if (startingUserId != -1) {
-            SystemService.TargetUser mockedTargetUser =
-                    ExtendedMockito.mock(SystemService.TargetUser.class);
-            doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier();
-            service.onUserStarting(mockedTargetUser);
+    }
+
+    @Test
+    public void testSetState() {
+        MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.WARN)
+                .spyStatic(PersistedState.class)
+                .startMocking();
+        try {
+            PersistedState persistedState = mock(PersistedState.class);
+            doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
+
+            SensorPrivacyStateController sensorPrivacyStateController =
+                    getSensorPrivacyStateControllerImpl();
+
+            sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+                    mock(Handler.class), changed -> {});
+
+            ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class);
+
+            verify(persistedState, times(1)).setState(eq(SOFTWARE), eq(0), eq(MICROPHONE),
+                    captor.capture());
+            assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
+        } finally {
+            mockitoSession.finishMocking();
         }
-        onDeviceFile.delete();
+    }
+
+    private SensorPrivacyStateController getSensorPrivacyStateControllerImpl() {
+        SensorPrivacyStateControllerImpl.getInstance().resetForTestingImpl();
+        return SensorPrivacyStateControllerImpl.getInstance();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index e40f543..e1aa08d 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -23,6 +23,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.returnsArgAt;
 import static org.mockito.ArgumentMatchers.any;
@@ -40,6 +41,7 @@
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
+import android.apphibernation.HibernationStats;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -68,6 +70,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -126,7 +130,7 @@
         mUsageEventListener = mUsageEventListenerCaptor.getValue();
 
         doReturn(mUserInfos).when(mUserManager).getUsers();
-
+        doReturn(true).when(mPackageManagerInternal).canQueryPackage(anyInt(), any());
         doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
                 anyInt(), anyBoolean(), anyBoolean(), any(), any());
 
@@ -376,6 +380,58 @@
         assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
     }
 
+    @Test
+    public void testGetHibernationStatsForUser_getsStatsForPackage() {
+        // GIVEN a package is hibernating globally and for a user
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+        // WHEN we ask for the hibernation stats for the package
+        Map<String, HibernationStats> stats =
+                mAppHibernationService.getHibernationStatsForUser(
+                        Set.of(PACKAGE_NAME_1), USER_ID_1);
+
+        // THEN the stats exist for the package
+        assertTrue(stats.containsKey(PACKAGE_NAME_1));
+    }
+
+    @Test
+    public void testGetHibernationStatsForUser_noExceptionThrownWhenPackageDoesntExist() {
+        // WHEN we ask for the hibernation stats for a package that doesn't exist
+        Map<String, HibernationStats> stats =
+                mAppHibernationService.getHibernationStatsForUser(
+                        Set.of(PACKAGE_NAME_1), USER_ID_1);
+
+        // THEN no exception is thrown and empty stats are returned
+        assertNotNull(stats);
+    }
+
+    @Test
+    public void testGetHibernationStatsForUser_returnsAllIfNoPackagesSpecified()
+            throws RemoteException {
+        // GIVEN an unlocked user with all packages installed and they're all hibernating
+        UserInfo userInfo =
+                addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_2, true);
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_3, true);
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_3, USER_ID_2, true);
+
+        // WHEN we ask for the hibernation stats with no package specified
+        Map<String, HibernationStats> stats =
+                mAppHibernationService.getHibernationStatsForUser(
+                        null /* packageNames */, USER_ID_2);
+
+        // THEN all the package stats are returned
+        assertTrue(stats.containsKey(PACKAGE_NAME_1));
+        assertTrue(stats.containsKey(PACKAGE_NAME_2));
+        assertTrue(stats.containsKey(PACKAGE_NAME_3));
+    }
+
     /**
      * Mock a usage event occurring.
      *
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
new file mode 100644
index 0000000..0ed90d2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -0,0 +1,321 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.BrightnessInfo;
+import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Temperature.ThrottlingStatus;
+import android.os.Temperature;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.BrightnessThrottler.Injector;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BrightnessThrottlerTest {
+    private static final float EPSILON = 0.000001f;
+
+    private Handler mHandler;
+    private TestLooper mTestLooper;
+
+    @Mock IThermalService mThermalServiceMock;
+    @Mock Injector mInjectorMock;
+
+    @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
+        mTestLooper = new TestLooper();
+        mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message msg) {
+                return true;
+            }
+        });
+
+    }
+
+    /////////////////
+    // Test Methods
+    /////////////////
+
+    @Test
+    public void testBrightnessThrottlingData() {
+        List<ThrottlingLevel> singleLevel = new ArrayList<>();
+        singleLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+
+        List<ThrottlingLevel> validLevels = new ArrayList<>();
+        validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
+        validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+
+        List<ThrottlingLevel> unsortedThermalLevels = new ArrayList<>();
+        unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
+        unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
+
+        List<ThrottlingLevel> unsortedBrightnessLevels = new ArrayList<>();
+        unsortedBrightnessLevels.add(
+                new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
+        unsortedBrightnessLevels.add(
+                new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
+
+        List<ThrottlingLevel> unsortedLevels = new ArrayList<>();
+        unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
+        unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
+
+        List<ThrottlingLevel> invalidLevel = new ArrayList<>();
+        invalidLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+                PowerManager.BRIGHTNESS_MAX + EPSILON));
+
+        // Test invalid data
+        BrightnessThrottlingData data;
+        data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null);
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create((BrightnessThrottlingData)null);
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create(unsortedThermalLevels);
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create(unsortedBrightnessLevels);
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create(unsortedLevels);
+        assertEquals(data, null);
+        data = BrightnessThrottlingData.create(invalidLevel);
+        assertEquals(data, null);
+
+        // Test valid data
+        data = BrightnessThrottlingData.create(singleLevel);
+        assertNotEquals(data, null);
+        assertThrottlingLevelsEquals(singleLevel, data.throttlingLevels);
+
+        data = BrightnessThrottlingData.create(validLevels);
+        assertNotEquals(data, null);
+        assertThrottlingLevelsEquals(validLevels, data.throttlingLevels);
+    }
+
+    @Test
+    public void testThrottlingUnsupported() throws Exception {
+        final BrightnessThrottler throttler = createThrottlerUnsupported();
+        assertFalse(throttler.deviceSupportsThrottling());
+
+        // Thermal listener shouldn't be registered if throttling is unsupported
+        verify(mInjectorMock, never()).getThermalService();
+
+        // Ensure that brightness is uncapped when the device doesn't support throttling
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+    }
+
+    @Test
+    public void testThrottlingSingleLevel() throws Exception {
+        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+            0.25f);
+
+        List<ThrottlingLevel> levels = new ArrayList<>();
+        levels.add(level);
+        final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+        final BrightnessThrottler throttler = createThrottlerSupported(data);
+        assertTrue(throttler.deviceSupportsThrottling());
+
+        verify(mThermalServiceMock).registerThermalEventListenerWithType(
+                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+        // Set status too low to trigger throttling
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+        // Set status just high enough to trigger throttling
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+        mTestLooper.dispatchAll();
+        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Set status more than high enough to trigger throttling
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+           throttler.getBrightnessMaxReason());
+
+        // Return to the lower throttling level
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+        mTestLooper.dispatchAll();
+        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Cool down
+        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
+            throttler.getBrightnessMaxReason());
+    }
+
+    @Test
+    public void testThrottlingMultiLevel() throws Exception {
+        final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
+            0.62f);
+        final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+            0.25f);
+
+        List<ThrottlingLevel> levels = new ArrayList<>();
+        levels.add(levelLo);
+        levels.add(levelHi);
+        final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+        final BrightnessThrottler throttler = createThrottlerSupported(data);
+        assertTrue(throttler.deviceSupportsThrottling());
+
+        verify(mThermalServiceMock).registerThermalEventListenerWithType(
+                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+        // Set status too low to trigger throttling
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+        // Set status just high enough to trigger throttling
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
+        mTestLooper.dispatchAll();
+        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Set status to an intermediate throttling level
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Set status to the highest configured throttling level
+        listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
+        mTestLooper.dispatchAll();
+        assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Set status to exceed the highest configured throttling level
+        listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Return to an intermediate throttling level
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
+        mTestLooper.dispatchAll();
+        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Return to the lowest configured throttling level
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
+        mTestLooper.dispatchAll();
+        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
+        assertTrue(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+            throttler.getBrightnessMaxReason());
+
+        // Cool down
+        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
+        mTestLooper.dispatchAll();
+        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+        assertFalse(throttler.isThrottled());
+        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+    }
+
+    private void assertThrottlingLevelsEquals(
+            List<ThrottlingLevel> expected,
+            List<ThrottlingLevel> actual) {
+        assertEquals(expected.size(), actual.size());
+
+        for (int i = 0; i < expected.size(); i++) {
+            ThrottlingLevel expectedLevel = expected.get(i);
+            ThrottlingLevel actualLevel = actual.get(i);
+
+            assertEquals(expectedLevel.thermalStatus, actualLevel.thermalStatus);
+            assertEquals(expectedLevel.brightness, actualLevel.brightness, 0.0f);
+        }
+    }
+
+    private BrightnessThrottler createThrottlerUnsupported() {
+        return new BrightnessThrottler(mInjectorMock, mHandler, null, () -> {});
+    }
+
+    private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
+        assertNotNull(data);
+        return new BrightnessThrottler(mInjectorMock, mHandler, data, () -> {});
+    }
+
+    private Temperature getSkinTemp(@ThrottlingStatus int status) {
+        return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 418831f..40c0392 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1461,7 +1461,8 @@
         // Turn on HBM, with brightness in the HBM range
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
-                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, hbmRefreshRate);
@@ -1469,7 +1470,8 @@
         // Turn on HBM, with brightness below the HBM range
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
-                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1477,7 +1479,8 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1485,7 +1488,8 @@
         // Turn on HBM, with brightness in the HBM range
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
-                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, hbmRefreshRate);
@@ -1493,7 +1497,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1501,7 +1505,8 @@
         // Turn on HBM, with brightness below the HBM range
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
-                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1509,7 +1514,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1580,7 +1585,7 @@
         // Turn on HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, initialRefreshRate);
@@ -1598,7 +1603,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1606,7 +1611,7 @@
         // Turn HBM on again and ensure the updated vote value stuck
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, updatedRefreshRate);
@@ -1622,7 +1627,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1654,7 +1659,7 @@
         // Turn on HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1662,7 +1667,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1694,7 +1699,7 @@
         // Turn on HBM when HBM is supported; expect a valid transition point and a vote.
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, 60.0f);
@@ -1702,7 +1707,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1711,7 +1716,7 @@
         // no vote.
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    HBM_TRANSITION_POINT_INVALID));
+                    HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1720,7 +1725,7 @@
         // no vote.
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
-                    HBM_TRANSITION_POINT_INVALID));
+                    HBM_TRANSITION_POINT_INVALID, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1728,7 +1733,7 @@
         // Turn off HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertNull(vote);
@@ -1737,7 +1742,8 @@
     private void setHbmAndAssertRefreshRate(
             DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
         when(mInjector.getBrightnessInfo(DISPLAY_ID))
-                .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT));
+                .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
 
         final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1817,7 +1823,7 @@
         // Turn on HBM
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
-                    TRANSITION_POINT));
+                    TRANSITION_POINT, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
         assertVoteForRefreshRate(vote, 60.f);
@@ -1978,7 +1984,8 @@
 
         when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
                 new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
-                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT));
+                    BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT,
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
         listener.onDisplayChanged(DISPLAY_ID);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 4bb5d74..b7af010 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
@@ -201,7 +203,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
 
         // Verify we are in HBM
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -233,7 +235,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
 
         // Verify we are in HBM
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -258,18 +260,18 @@
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
 
         // Verify we are in HBM
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
 
-        hbmc.onBrightnessChanged(TRANSITION_POINT - 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT - 0.01f);
         advanceTime(1);
 
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
 
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
 
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -288,13 +290,13 @@
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
         // Go into HBM for half the allowed window
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
 
         // Move lux below threshold (ending first event);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT);
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
 
         // Move up some amount of time so that there's still time in the window even after a
@@ -304,7 +306,7 @@
 
         // Go into HBM for just under the second half of allowed window
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 1);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 1);
         advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1);
 
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -434,7 +436,7 @@
         float brightness = 0.5f;
         float expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT,
                 DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range
-        hbmc.onBrightnessChanged(brightness);
+        hbmcOnBrightnessChanged(hbmc, brightness);
         advanceTime(0);
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
 
@@ -442,21 +444,21 @@
         brightness = 0.33f;
         expectedHdrBrightness = MathUtils.map(DEFAULT_MIN, TRANSITION_POINT,
                 DEFAULT_MIN, DEFAULT_MAX, brightness); // map value from normal range to hdr range
-        hbmc.onBrightnessChanged(brightness);
+        hbmcOnBrightnessChanged(hbmc, brightness);
         advanceTime(0);
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
 
         // Try the min value
         brightness = DEFAULT_MIN;
         expectedHdrBrightness = DEFAULT_MIN;
-        hbmc.onBrightnessChanged(brightness);
+        hbmcOnBrightnessChanged(hbmc, brightness);
         advanceTime(0);
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
 
         // Try the max value
         brightness = TRANSITION_POINT;
         expectedHdrBrightness = DEFAULT_MAX;
-        hbmc.onBrightnessChanged(brightness);
+        hbmcOnBrightnessChanged(hbmc, brightness);
         advanceTime(0);
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
     }
@@ -467,7 +469,7 @@
         final int displayStatsId = mDisplayUniqueId.hashCode();
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
-        hbmc.onBrightnessChanged(TRANSITION_POINT);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT);
         hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
                 DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
         advanceTime(0);
@@ -489,7 +491,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
 
         // Verify Stats HBM_ON_SUNLIGHT
         verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
@@ -506,12 +508,12 @@
     }
 
     @Test
-    public void tetHbmStats_NbmHdrNoReport() {
+    public void testHbmStats_NbmHdrNoReport() {
         final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
         final int displayStatsId = mDisplayUniqueId.hashCode();
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
-        hbmc.onBrightnessChanged(DEFAULT_MIN);
+        hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
         hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
                 DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
         advanceTime(0);
@@ -524,7 +526,27 @@
     }
 
     @Test
-    public void testHbmStats_ThermalOff() throws Exception {
+    public void testHbmStats_HighLuxLowBrightnessNoReport() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
+        advanceTime(0);
+        // verify in HBM sunlight mode
+        assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+
+        // Verify Stats HBM_ON_SUNLIGHT not report
+        verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            anyInt());
+    }
+
+    // Test reporting of thermal throttling when triggered by HighBrightnessModeController's
+    // internal thermal throttling.
+    @Test
+    public void testHbmStats_InternalThermalOff() throws Exception {
         final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
         final int displayStatsId = mDisplayUniqueId.hashCode();
 
@@ -534,7 +556,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(1);
         verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
             eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -548,6 +570,37 @@
             eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
     }
 
+    // Test reporting of thermal throttling when triggered externally through
+    // HighBrightnessModeController.onBrightnessChanged()
+    @Test
+    public void testHbmStats_ExternalThermalOff() throws Exception {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+        final float hbmBrightness = TRANSITION_POINT + 0.01f;
+        final float nbmBrightness = TRANSITION_POINT - 0.01f;
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        // Brightness is unthrottled, HBM brightness granted
+        hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_NONE);
+        advanceTime(1);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        // Brightness is thermally throttled, HBM brightness denied (NBM brightness granted)
+        hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_THERMAL);
+        advanceTime(1);
+        // We expect HBM mode to remain set to sunlight, indicating that HBMC *allows* this mode.
+        // However, we expect the HBM state reported by HBMC to be off, since external thermal
+        // throttling (reported to HBMC through onBrightnessChanged()) lowers brightness to below
+        // the HBM transition point.
+        assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+    }
+
     @Test
     public void testHbmStats_TimeOut() {
         final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
@@ -555,7 +608,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(0);
         verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
             eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -576,7 +629,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(0);
         verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
             eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -595,7 +648,7 @@
 
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
         advanceTime(0);
         verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
             eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
@@ -649,4 +702,8 @@
     private Temperature getSkinTemp(@ThrottlingStatus int status) {
         return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
     }
+
+    private void hbmcOnBrightnessChanged(HighBrightnessModeController hbmc, float brightness) {
+        hbmc.onBrightnessChanged(brightness, brightness, BRIGHTNESS_MAX_REASON_NONE);
+    }
 }
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 e80721a..94cf20f 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -51,10 +51,9 @@
 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_CARRIER;
 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;
+import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -205,6 +204,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TimeZone;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -229,10 +229,12 @@
     private static final int TEST_SUB_ID = 42;
     private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
 
-
-    private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
+    private static NetworkTemplate sTemplateWifi = new NetworkTemplate.Builder(MATCH_WIFI)
+            .setWifiNetworkKeys(Set.of(TEST_WIFI_NETWORK_KEY)).build();
     private static NetworkTemplate sTemplateCarrierMetered =
-            buildTemplateCarrierMetered(TEST_IMSI);
+            new NetworkTemplate.Builder(MATCH_CARRIER)
+                    .setSubscriberIds(Set.of(TEST_IMSI))
+                    .setMeteredness(METERED_YES).build();
 
     /**
      * Path on assets where files used by {@link NetPolicyXml} are located.
@@ -1160,11 +1162,12 @@
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
         setNetworkPolicies(new NetworkPolicy(
-                sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
+                sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, DataUnit.MEBIBYTES.toBytes(1),
+                DataUnit.MEBIBYTES.toBytes(2), false));
         mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
 
         verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
-                (2 * MB_IN_BYTES) - 512);
+                DataUnit.MEBIBYTES.toBytes(2) - 512);
     }
 
     @Test
@@ -1252,7 +1255,7 @@
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
 
-            mService.snoozeLimit(NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI));
+            mService.snoozeLimit(sTemplateCarrierMetered);
             mService.updateNetworks();
 
             verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
@@ -1955,7 +1958,7 @@
         assertEquals("Unexpected number of network policies", 1, policies.length);
         NetworkPolicy actualPolicy = policies[0];
         assertEquals("Unexpected template match rule in network policies",
-                NetworkTemplate.MATCH_WIFI,
+                MATCH_WIFI,
                 actualPolicy.template.getMatchRule());
         assertEquals("Unexpected subscriberIds size in network policies",
                 actualPolicy.template.getSubscriberIds().size(), 0);
@@ -2026,7 +2029,10 @@
 
     private static NetworkPolicy buildFakeCarrierPolicy(int cycleDay, long warningBytes,
             long limitBytes, boolean inferred) {
-        final NetworkTemplate template = buildTemplateCarrierMetered(FAKE_SUBSCRIBER_ID);
+        // TODO: Refactor this to use sTemplateCarrierMetered.
+        final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER)
+                .setSubscriberIds(Set.of(FAKE_SUBSCRIBER_ID))
+                .setMeteredness(METERED_YES).build();
         return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes,
                 limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6c9a60a..ac83642 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -315,18 +315,19 @@
                 .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
                 .build();
 
-        ps1.addOrUpdateSuspension("suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1,
-                0);
-        ps1.addOrUpdateSuspension("suspendingPackage2", dialogInfo2, appExtras2, launcherExtras2,
-                0);
+        ps1.modifyUserState(0).putSuspendParams( "suspendingPackage1",
+                SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+        ps1.modifyUserState(0).putSuspendParams( "suspendingPackage2",
+                SuspendParams.getInstanceOrNull(dialogInfo2, appExtras2, launcherExtras2));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
         watcher.verifyChangeReported("put package 1");
 
-        ps2.addOrUpdateSuspension("suspendingPackage3", null, appExtras1, null, 0);
+        ps2.modifyUserState(0).putSuspendParams( "suspendingPackage3",
+                SuspendParams.getInstanceOrNull(null, appExtras1, null));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
         watcher.verifyChangeReported("put package 2");
 
-        ps3.removeSuspension("irrelevant", 0);
+        ps3.modifyUserState(0).removeSuspension("irrelevant");
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
         watcher.verifyChangeReported("put package 3");
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 1e4134e..6c72369 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -41,6 +41,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -323,30 +324,31 @@
     }
     @Test
     public void testPackageUseReasons() throws Exception {
-        final PackageStateUnserialized testState1 = new PackageStateUnserialized();
+        PackageSetting packageSetting = Mockito.mock(PackageSetting.class);
+        final PackageStateUnserialized testState1 = new PackageStateUnserialized(packageSetting);
         testState1.setLastPackageUsageTimeInMills(-1, 10L);
         assertLastPackageUsageUnset(testState1);
 
-        final PackageStateUnserialized testState2 = new PackageStateUnserialized();
+        final PackageStateUnserialized testState2 = new PackageStateUnserialized(packageSetting);
         testState2.setLastPackageUsageTimeInMills(
                 PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT, 20L);
         assertLastPackageUsageUnset(testState2);
 
-        final PackageStateUnserialized testState3 = new PackageStateUnserialized();
+        final PackageStateUnserialized testState3 = new PackageStateUnserialized(packageSetting);
         testState3.setLastPackageUsageTimeInMills(Integer.MAX_VALUE, 30L);
         assertLastPackageUsageUnset(testState3);
 
-        final PackageStateUnserialized testState4 = new PackageStateUnserialized();
+        final PackageStateUnserialized testState4 = new PackageStateUnserialized(packageSetting);
         testState4.setLastPackageUsageTimeInMills(0, 40L);
         assertLastPackageUsageSet(testState4, 0, 40L);
 
-        final PackageStateUnserialized testState5 = new PackageStateUnserialized();
+        final PackageStateUnserialized testState5 = new PackageStateUnserialized(packageSetting);
         testState5.setLastPackageUsageTimeInMills(
                 PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L);
         assertLastPackageUsageSet(
                 testState5, PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L);
 
-        final PackageStateUnserialized testState6 = new PackageStateUnserialized();
+        final PackageStateUnserialized testState6 = new PackageStateUnserialized(packageSetting);
         testState6.setLastPackageUsageTimeInMills(
                 PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L);
         assertLastPackageUsageSet(
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
new file mode 100644
index 0000000..4ae9613
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.IPowerManager;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link com.android.server.power.LowPowerStandbyController}.
+ *
+ * Build/Install/Run:
+ * atest LowPowerStandbyControllerTest
+ */
+public class LowPowerStandbyControllerTest {
+    private static final int STANDBY_TIMEOUT = 5000;
+
+    private LowPowerStandbyController mController;
+    private BroadcastInterceptingContext mContextSpy;
+    private Resources mResourcesSpy;
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+
+    @Mock
+    private AlarmManager mAlarmManagerMock;
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private PowerManagerInternal mPowerManagerInternalMock;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry.getContext()));
+        when(mContextSpy.getSystemService(AlarmManager.class)).thenReturn(mAlarmManagerMock);
+        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null, null);
+        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+
+        when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+        mResourcesSpy = spy(mContextSpy.getResources());
+        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_lowPowerStandbySupported))
+                .thenReturn(true);
+        when(mResourcesSpy.getInteger(
+                com.android.internal.R.integer.config_lowPowerStandbyNonInteractiveTimeout))
+                .thenReturn(STANDBY_TIMEOUT);
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_lowPowerStandbyEnabledByDefault))
+                .thenReturn(false);
+
+        FakeSettingsProvider.clearSettingsProvider();
+        MockContentResolver cr = new MockContentResolver(mContextSpy);
+        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContextSpy.getContentResolver()).thenReturn(cr);
+
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+
+        mController = new LowPowerStandbyController(mContextSpy, mTestLooper.getLooper(),
+                () -> mClock.now());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
+    }
+
+    @Test
+    public void testOnSystemReady_isInactivate() {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+    }
+
+    @Test
+    public void testActivate() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+        setNonInteractive();
+        setDeviceIdleMode(true);
+        awaitStandbyTimeoutAlarm();
+        assertThat(mController.isActive()).isTrue();
+        verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
+    }
+
+    private void awaitStandbyTimeoutAlarm() {
+        ArgumentCaptor<Long> timeArg = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<AlarmManager.OnAlarmListener> listenerArg =
+                ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+        verify(mAlarmManagerMock).setExact(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                timeArg.capture(), anyString(),
+                listenerArg.capture(), any());
+        mClock.reset();
+        mClock.fastForward(timeArg.getValue());
+        listenerArg.getValue().onAlarm();
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testOnNonInteractive_notImmediatelyActive() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+
+        setNonInteractive();
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+    }
+
+    @Test
+    public void testOnNonInteractive_activateAfterStandbyTimeout() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+
+        setNonInteractive();
+        awaitStandbyTimeoutAlarm();
+
+        assertThat(mController.isActive()).isTrue();
+        verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
+    }
+
+    @Test
+    public void testOnNonInteractive_doesNotActivateWhenBecomingInteractive() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+
+        setNonInteractive();
+        advanceTime(STANDBY_TIMEOUT / 2);
+        setInteractive();
+        verifyStandbyAlarmCancelled();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+    }
+
+    private void verifyStandbyAlarmCancelled() {
+        InOrder inOrder = inOrder(mAlarmManagerMock);
+        inOrder.verify(mAlarmManagerMock, atLeast(0)).setExact(anyInt(), anyLong(), anyString(),
+                any(), any());
+        inOrder.verify(mAlarmManagerMock).cancel((AlarmManager.OnAlarmListener) any());
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testOnInteractive_deactivate() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+        setNonInteractive();
+        setDeviceIdleMode(true);
+        awaitStandbyTimeoutAlarm();
+
+        setInteractive();
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
+    }
+
+    @Test
+    public void testOnDozeMaintenance_deactivate() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+        mController.setActiveDuringMaintenance(false);
+        setNonInteractive();
+        setDeviceIdleMode(true);
+        awaitStandbyTimeoutAlarm();
+
+        setDeviceIdleMode(false);
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
+    }
+
+    @Test
+    public void testOnDozeMaintenance_activeDuringMaintenance_staysActive() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+        mController.setActiveDuringMaintenance(true);
+        setNonInteractive();
+        setDeviceIdleMode(true);
+        awaitStandbyTimeoutAlarm();
+
+        setDeviceIdleMode(false);
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isTrue();
+        verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(false);
+    }
+
+    @Test
+    public void testOnDozeMaintenanceEnds_activate() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(true);
+        setNonInteractive();
+        setDeviceIdleMode(true);
+        awaitStandbyTimeoutAlarm();
+
+        setDeviceIdleMode(false);
+        advanceTime(1000);
+        setDeviceIdleMode(true);
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isTrue();
+        verify(mPowerManagerInternalMock, times(2)).setLowPowerStandbyActive(true);
+    }
+
+    @Test
+    public void testLowPowerStandbyDisabled_doesNotActivate() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+        mController.setEnabled(false);
+        setNonInteractive();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mAlarmManagerMock, never()).setExact(anyInt(), anyLong(), anyString(), any(), any());
+        verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
+    }
+
+    @Test
+    public void testLowPowerStandbyEnabled_EnabledChangedBroadcastsAreSent() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+
+        BroadcastInterceptingContext.FutureIntent futureIntent = mContextSpy.nextBroadcastIntent(
+                PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+        mController.setEnabled(false);
+        futureIntent.assertNotReceived();
+
+        futureIntent = mContextSpy.nextBroadcastIntent(
+                PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+        mController.setEnabled(true);
+        assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+
+        futureIntent = mContextSpy.nextBroadcastIntent(
+                PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+        mController.setEnabled(true);
+        futureIntent.assertNotReceived();
+
+        futureIntent = mContextSpy.nextBroadcastIntent(
+                PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
+
+        mController.setEnabled(false);
+        assertThat(futureIntent.get(1, TimeUnit.SECONDS)).isNotNull();
+    }
+
+    @Test
+    public void testSetEnabled_WhenNotSupported_DoesNotEnable() throws Exception {
+        setLowPowerStandbySupportedConfig(false);
+        mController.systemReady();
+
+        mController.setEnabled(true);
+
+        assertThat(mController.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void testIsSupported_WhenSupported() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+
+        assertThat(mController.isSupported()).isTrue();
+    }
+
+    @Test
+    public void testIsSupported_WhenNotSupported() throws Exception {
+        setLowPowerStandbySupportedConfig(false);
+        mController.systemReady();
+
+        assertThat(mController.isSupported()).isFalse();
+    }
+
+    @Test
+    public void testAllowlistChange_servicesAreNotified() throws Exception {
+        setLowPowerStandbySupportedConfig(true);
+        mController.systemReady();
+
+        LowPowerStandbyControllerInternal service = LocalServices.getService(
+                LowPowerStandbyControllerInternal.class);
+        service.addToAllowlist(10);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {10});
+
+        service.removeFromAllowlist(10);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {});
+    }
+
+    @Test
+    public void testForceActive() throws Exception {
+        setLowPowerStandbySupportedConfig(false);
+        mController.systemReady();
+
+        mController.forceActive(true);
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isTrue();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyActive(true);
+
+        mController.forceActive(false);
+        mTestLooper.dispatchAll();
+
+        assertThat(mController.isActive()).isFalse();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyActive(false);
+    }
+
+    private void setLowPowerStandbySupportedConfig(boolean supported) {
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_lowPowerStandbySupported))
+                .thenReturn(supported);
+    }
+
+    private void setInteractive() throws Exception {
+        when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+        mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+    }
+
+    private void setNonInteractive() throws Exception {
+        when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+        mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+    }
+
+    private void setDeviceIdleMode(boolean idle) throws Exception {
+        when(mIPowerManagerMock.isDeviceIdleMode()).thenReturn(idle);
+        mContextSpy.sendBroadcast(new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
+    }
+
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index d35c679..51dbd97 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.power;
 
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -65,6 +67,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
 import android.provider.Settings;
@@ -86,6 +89,7 @@
 import com.android.server.power.PowerManagerService.Injector;
 import com.android.server.power.PowerManagerService.NativeWrapper;
 import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
+import com.android.server.power.PowerManagerService.WakeLock;
 import com.android.server.power.batterysaver.BatterySaverController;
 import com.android.server.power.batterysaver.BatterySaverPolicy;
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
@@ -283,6 +287,13 @@
             void invalidateIsInteractiveCaches() {
                 // Avoids an SELinux failure.
             }
+
+            @Override
+            LowPowerStandbyController createLowPowerStandbyController(Context context,
+                    Looper looper) {
+                return new LowPowerStandbyController(context, mTestLooper.getLooper(),
+                        SystemClock::elapsedRealtime);
+            }
         });
         return mService;
     }
@@ -293,6 +304,7 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.removeServiceForTest(BatteryManagerInternal.class);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
         FakeSettingsProvider.clearSettingsProvider();
     }
 
@@ -1575,4 +1587,60 @@
                 .setFullPowerSavePolicy(mockSetPolicyConfig)).isTrue();
         verify(mBatterySaverStateMachineMock).setFullBatterySaverPolicy(eq(mockSetPolicyConfig));
     }
+
+    @Test
+    public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
+        createService();
+        mService.systemReady();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+        mService.setDeviceIdleModeInternal(true);
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
+        createService();
+        mService.systemReady();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+        mService.setDeviceIdleModeInternal(true);
+        mService.setLowPowerStandbyActiveInternal(true);
+
+        assertThat(wakeLock.mDisabled).isTrue();
+    }
+
+    @Test
+    public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
+        createService();
+        mService.systemReady();
+        WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
+        mService.setDeviceIdleModeInternal(true);
+        mService.setLowPowerStandbyActiveInternal(true);
+        mService.setLowPowerStandbyAllowlistInternal(new int[]{wakeLock.mOwnerUid});
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
+        createService();
+        mService.systemReady();
+        WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
+        mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
+        mService.setDeviceIdleModeInternal(true);
+        mService.setLowPowerStandbyActiveInternal(true);
+
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    private WakeLock acquireWakeLock(String tag, int flags) {
+        IBinder token = new Binder();
+        String packageName = "pkg.name";
+        mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+                null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+        return mService.findWakeLockLocked(token);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
index 09612e3..a73fcb8 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -211,6 +211,25 @@
                 dumpLog(log, false));
     }
 
+    @Test
+    public void testAddSystemWakelock() {
+        final int tagDatabaseSize = 6;
+        final int logSize = 10;
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        WakeLockLog log = new WakeLockLog(injectorSpy);
+
+        when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+        log.onWakeLockAcquired("TagPartial", 101,
+                PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK);
+
+        assertEquals("Wake Lock Log\n"
+                        + "  01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,system-wakelock)\n"
+                        + "  -\n"
+                        + "  Events: 1, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 3\n",
+                dumpLog(log, false));
+    }
+
     private String dumpLog(WakeLockLog log, boolean includeTagDb) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
index a74615d..22d383a 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
@@ -107,6 +107,13 @@
         }
     }
 
+    /**
+     * Deletes all messages in queue.
+     */
+    public void clear() {
+        mMessages.clear();
+    }
+
     public PriorityQueue<MsgInfo> getPendingMessages() {
         return new PriorityQueue<>(mMessages);
     }
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 ea03250..efc9a49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -106,7 +106,6 @@
 
 import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
-import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -1666,34 +1665,6 @@
     }
 
     @Test
-    public void testClearIntermediateFixedRotationAdjustments() throws RemoteException {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        mDisplayContent.setFixedRotationLaunchingApp(activity,
-                (mDisplayContent.getRotation() + 1) % 4);
-        // Create a window so FixedRotationAdjustmentsItem can be sent.
-        createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin");
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        activity2.setVisible(false);
-        clearInvocations(mAtm.getLifecycleManager());
-        // The first activity has applied fixed rotation but the second activity becomes the top
-        // before the transition is done and it has the same rotation as display, so the dispatched
-        // rotation adjustment of first activity must be cleared.
-        mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2,
-                false /* checkOpening */);
-
-        final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor =
-                ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class);
-        verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
-                eq(activity.app.getThread()), adjustmentsCaptor.capture());
-        // The transformation is kept for animation in real case.
-        assertTrue(activity.hasFixedRotationTransform());
-        final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain(
-                activity.token, null /* fixedRotationAdjustments */);
-        // The captor may match other items. The first one must be the item to clear adjustments.
-        assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0));
-    }
-
-    @Test
     public void testRemoteRotation() {
         DisplayContent dc = createNewDisplay();
 
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index aa01f31..24fda17 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -16,6 +16,8 @@
 
 package com.android.server.usage;
 
+import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
+
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -23,9 +25,11 @@
 import android.annotation.UserIdInt;
 import android.app.usage.BroadcastResponseStats;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
@@ -69,6 +73,12 @@
     void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
             UserHandle targetUser, long idForResponseEvent,
             @ElapsedRealtimeLong long timestampMs) {
+        if (DEBUG_RESPONSE_STATS) {
+            Slog.d(TAG, TextUtils.formatSimple(
+                    "reportBroadcastDispatchEvent; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s",
+                    sourceUid, targetPackage, targetUser, idForResponseEvent,
+                    TimeUtils.formatDuration(timestampMs)));
+        }
         synchronized (mLock) {
             final LongSparseArray<BroadcastEvent> broadcastEvents =
                     getOrCreateBroadcastEventsLocked(targetPackage, targetUser);
@@ -99,6 +109,12 @@
 
     private void reportNotificationEvent(@NotificationEvent int event,
             @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+        if (DEBUG_RESPONSE_STATS) {
+            Slog.d(TAG, TextUtils.formatSimple(
+                    "reportNotificationEvent; event=<%s>, pkg=%s, usr=%d, ts=%s",
+                    notificationEventToString(event), packageName, user.getIdentifier(),
+                    TimeUtils.formatDuration(timestampMs)));
+        }
         // TODO (206518114): Store last N events to dump for debugging purposes.
         synchronized (mLock) {
             final LongSparseArray<BroadcastEvent> broadcastEvents =
@@ -288,6 +304,20 @@
         return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
     }
 
+    @NonNull
+    private String notificationEventToString(@NotificationEvent int event) {
+        switch (event) {
+            case NOTIFICATION_EVENT_POSTED:
+                return "posted";
+            case NOTIFICATION_EVENT_UPDATED:
+                return "updated";
+            case NOTIFICATION_EVENT_CANCELLED:
+                return "cancelled";
+            default:
+                return String.valueOf(event);
+        }
+    }
+
     void dump(@NonNull IndentingPrintWriter ipw) {
         ipw.println("Broadcast response stats:");
         ipw.increaseIndent();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e28839e..e90d28a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -89,6 +89,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -141,6 +142,7 @@
             = SystemProperties.getBoolean("persist.debug.time_correction", true);
 
     static final boolean DEBUG = false; // Never submit with true
+    static final boolean DEBUG_RESPONSE_STATS = DEBUG || Log.isLoggable(TAG, Log.DEBUG);
     static final boolean COMPRESS_TIME = false;
 
     private static final long TEN_SECONDS = 10 * 1000;
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
index db0c80f..13d404c 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
@@ -218,14 +218,13 @@
             if (doesInterfaceContainInput
                     && doesInterfaceContainOutput) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-                if (!connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true)) {
-                    Log.d(TAG, "Can't claim control interface");
-                    continue;
-                }
-                int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
-                        interfaceDescriptor.getInterfaceNumber(),
-                        interfaceDescriptor.getAlternateSetting());
 
+                // The ALSA does not handle switching to the MIDI 2.0 interface correctly
+                // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
+                // Thus, simply use the control interface (interface zero).
+                int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
+                        0,
+                        interfaceDescriptor.getAlternateSetting());
                 connection.close();
                 return defaultMidiProtocol;
             }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 90ccec8..a061618 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -69,6 +69,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.AssistDataRequester;
 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+import com.android.server.power.LowPowerStandbyControllerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.ActivityAssistInfo;
@@ -89,6 +90,11 @@
     static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
             System.getProperty("vendor.powerhal.interaction.max", "200"));
     static final int BOOST_TIMEOUT_MS = 300;
+    /**
+     * The maximum time an app can stay on the Low Power Standby allowlist when
+     * the session is shown. There to safeguard against apps that don't call hide.
+     */
+    private static final int LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS = 120_000;
     // TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
     //  in the future.
     static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
@@ -124,6 +130,10 @@
             Executors.newSingleThreadScheduledExecutor();
     private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
     private final PowerManagerInternal mPowerManagerInternal;
+    private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal;
+    private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable =
+            this::removeFromLowPowerStandbyAllowlist;
+    private boolean mLowPowerStandbyAllowlisted;
     private PowerBoostSetter mSetPowerBoostRunnable;
     private final Handler mFgHandler;
 
@@ -211,6 +221,8 @@
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+        mLowPowerStandbyControllerInternal = LocalServices.getService(
+                LowPowerStandbyControllerInternal.class);
         mAppOps = context.getSystemService(AppOpsManager.class);
         mFgHandler = FgThread.getHandler();
         mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
@@ -322,6 +334,15 @@
             mSetPowerBoostRunnable = new PowerBoostSetter(
                     Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
             mFgHandler.post(mSetPowerBoostRunnable);
+
+            if (mLowPowerStandbyControllerInternal != null) {
+                mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid);
+                mLowPowerStandbyAllowlisted = true;
+                mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
+                mFgHandler.postDelayed(mRemoveFromLowPowerStandbyAllowlistRunnable,
+                        LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS);
+            }
+
             mCallback.onSessionShown(this);
             return true;
         }
@@ -493,6 +514,9 @@
                 }
                 // A negative value indicates canceling previous boost.
                 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
+                if (mLowPowerStandbyControllerInternal != null) {
+                    removeFromLowPowerStandbyAllowlist();
+                }
                 mCallback.onSessionHidden(this);
             }
             if (mFullyBound) {
@@ -730,6 +754,16 @@
         }
     }
 
+    private void removeFromLowPowerStandbyAllowlist() {
+        synchronized (mLock) {
+            if (mLowPowerStandbyAllowlisted) {
+                mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
+                mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid);
+                mLowPowerStandbyAllowlisted = false;
+            }
+        }
+    }
+
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         synchronized (mLock) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7ba4b11..a74930b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8898,8 +8898,8 @@
         sDefaults.putStringArray(
                 KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY, new String[] {
                         "capabilities=eims, retry_interval=1000, maximum_retries=20",
-                        "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
-                                + "2254, maximum_retries=0", // No retry for those causes
+                        "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2252|"
+                                + "2253|2254, maximum_retries=0", // No retry for those causes
                         "capabilities=mms|supl|cbs, retry_interval=2000",
                         "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
                                 + "5000|10000|15000|20000|40000|60000|120000|240000|"
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 4e98f42..d4fa1dd 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -24,14 +24,21 @@
 java_test_host {
     name: "ApkVerityTest",
     srcs: ["src/**/*.java"],
-    libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+    libs: [
+        "tradefed",
+        "compatibility-tradefed",
+        "compatibility-host-util",
+    ],
     static_libs: [
         "block_device_writer_jar",
         "frameworks-base-hostutils",
     ],
-    test_suites: ["general-tests", "vts"],
-    target_required: [
-        "block_device_writer_module",
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    data_device_bins: [
+        "block_device_writer",
     ],
     data: [
         ":ApkVerityTestCertDer",
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 0b5f0f6..e5d009d 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -24,12 +24,7 @@
 }
 
 cc_test {
-    // Depending on how the test runs, the executable may be uploaded to different location.
-    // Before the bug in the file pusher is fixed, workaround by making the name unique.
-    // See b/124718249#comment12.
-    name: "block_device_writer_module",
-    stem: "block_device_writer",
-
+    name: "block_device_writer",
     srcs: ["block_device_writer.cpp"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -38,31 +33,25 @@
         "-Wextra",
         "-g",
     ],
-    shared_libs: ["libbase", "libutils"],
-    // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
-    // the uploader does not pick up the executable from correct output location. The following
-    // workaround allows the test to:
-    //  * upload the 32-bit exectuable for both 32 and 64 bits devices to use
-    //  * refer to the same executable name in Java
-    //  * no need to force the Java test to be archiecture specific.
-    //
-    // See b/145573317 for details.
-    multilib: {
-        lib32: {
-            suffix: "",
-        },
-        lib64: {
-            suffix: "64",  // not really used
-        },
-    },
+    shared_libs: [
+        "libbase",
+        "libutils",
+    ],
+    compile_multilib: "first",
 
     auto_gen_config: false,
-    test_suites: ["general-tests", "pts", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
     gtest: false,
 }
 
 java_library_host {
     name: "block_device_writer_jar",
     srcs: ["src/**/*.java"],
-    libs: ["tradefed", "junit"],
+    libs: [
+        "tradefed",
+        "junit",
+    ],
 }
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 5c2c15b..730daf3 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -32,7 +32,7 @@
  * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
  * 1. In Android.bp, add:
  * <pre>
- *     target_required: ["block_device_writer_module"],
+ *      data_device_bins: ["block_device_writer"],
  * </pre>
  * 2. In AndroidText.xml, add:
  * <pre>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 7443f0b..61df403 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -28,7 +28,10 @@
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -125,6 +128,21 @@
     @Test
     override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() {
+        assumeFalse(isShellTransitionsEnabled)
+        super.appWindowReplacesLauncherAsTopWindow()
+    }
+
+    @FlakyTest(bugId = 216266712)
+    @Test
+    fun appWindowReplacesLauncherAsTopWindow_shellTransit() {
+        assumeTrue(isShellTransitionsEnabled)
+        super.appWindowReplacesLauncherAsTopWindow()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
@@ -139,4 +157,4 @@
                 .getConfigNonRotationTests(repetitions = 5)
         }
     }
-}
\ No newline at end of file
+}